多核CPU上32位读取的原子性

2023-11-27

(注意:我根据我认为可能提供帮助的人在哪里添加了标签到这个问题,所以请不要喊:))

在我的 VS 2017 64 位项目中,我有一个 32 位长值m_lClosed。当我想更新它时,我使用其中之一Interlocked函数族。

考虑这段代码,在线程 #1 上执行

LONG lRet = InterlockedCompareExchange(&m_lClosed, 1, 0);   // Set m_lClosed to 1 provided it's currently 0

现在考虑在线程 #2 上执行的这段代码:

if (m_lClosed) // Do something

我知道在单个 CPU 上,这不会成为问题,因为更新是原子的,读取也是原子的(请参阅MSDN),因此线程抢占不能使变量处于部分更新的状态。但在多核 CPU 上,如果每个线程位于不同的 CPU 上,我们确实可以并行执行这两段代码。在这个例子中,我认为这不会是一个问题,但测试可能正在更新的东西仍然感觉不对。

这个网页告诉我多 CPU 上的原子性是通过以下方式实现的LOCK汇编指令,防止其他 CPU 访问该内存。这听起来像是我所需要的,但是为上面的 if 测试生成的汇编语言仅仅是

cmp   dword ptr [l],0  

... no LOCK指令就在眼前。

在这样的情况下,我们应该如何确保读取的原子性?

编辑 24/4/18

首先感谢大家对这个问题的关注。我在下面展示了实际的代码;我故意保持简单,以关注这一切的原子性,但显然,如果我从第一分钟就展示出这一切,那就更好了。

其次,实际代码所在的项目是VS2005项目;因此无法访问 C++11 原子。这就是为什么我没有在问题中添加 C++11 标签。我将 VS2017 与“临时”项目一起使用,这样每次我在学习时进行更改时都不必构建巨大的 VS2005 项目。另外,它是一个更好的 IDE。

是的,所以实际的代码位于 IOCP 驱动的服务器中,整个原子性是关于处理关闭的套接字:

class CConnection
{
    //...

    DWORD PostWSARecv()
    {
        if (!m_lClosed)
            return ::WSARecv(...);
        else
            return WSAESHUTDOWN;
    }

    bool SetClosed()
    {
        LONG lRet = InterlockedCompareExchange(&m_lClosed, 1, 0);   // Set m_lClosed to 1 provided it's currently 0
        // If the swap was carried out, the return value is the old value of m_lClosed, which should be 0.
        return lRet == 0;
    }

    SOCKET m_sock;
    LONG m_lClosed;
};

呼叫者将呼叫SetClosed();如果返回 true,则会调用::closesocket()等等。请不要质疑为什么会这样,它就是这样:)

考虑一下如果一个线程关闭套接字而另一个线程尝试发布套接字,会发生什么情况WSARecv()。你可能会认为WSARecv()将失败(套接字毕竟已关闭!);但是,如果与建立新连接怎么办?与我们刚刚关闭的套接字句柄相同- 然后我们将发布WSARecv()这会成功,但这对我的程序逻辑来说是致命的,因为我们现在将一个完全不同的连接与这个 CConnection 对象关联起来。因此,我有if (!m_lClosed)测试。你可能会说我不应该在多个线程中处理相同的连接,但这不是这个问题的重点 :)

这就是为什么我需要测试m_lClosed在我做之前WSARecv() call.

现在,显然,我只是设置m_lClosed到 1,所以读/写撕裂并不是真正的问题,但是这是我关心的原则。如果我设置怎么办m_lClosed到 2147483647,然后测试 2147483647?在这种情况下,读/写撕裂会产生更大的问题。


这实际上取决于您的编译器和运行的 CPU。

x86 CPU 将自动读取 32 位值,而无需LOCK如果内存地址正确对齐,则添加前缀。但是,您很可能需要某种记忆障碍如果该变量用作某些其他相关数据的锁定/计数,则控制 CPU 乱序执行。未对齐的数据可能无法以原子方式读取,尤其是当该值跨越页边界时。

如果您不是手工编码组装,您还需要担心编译器重新排序优化.

任何标记为的变量volatile当编译器(以及可能生成的机器代码)中将有排序约束使用 Visual C++ 编译:

_ReadBarrier、_WriteBarrier 和 _ReadWriteBarrier 编译器内在函数仅阻止编译器重新排序。在 Visual Studio 2003 中,易失性到易失性引用是有序的;编译器不会重新排序易失性变量的访问。在 Visual Studio 2005 中,编译器还使用获取语义对易失性变量进行读取操作,并使用释放语义对易失性变量进行写入操作(当 CPU 支持时)。

Microsoft 特定的 volatile 关键字增强功能:

当使用 /volatile:ms 编译器选项时(默认情况下,当目标架构不是 ARM 时),除了维护对其他全局对象的引用的顺序之外,编译器还会生成额外的代码来维护对 易失性对象的引用之间的顺序。尤其:

  • 对易失性对象的写入(也称为易失性写入)具有 Release 语义;也就是说,在指令序列中写入易失性对象之前发生的对全局或静态对象的引用将发生在已编译的二进制文件中的易失性写入之前。

  • 易失性对象的读取(也称为易失性读取)具有 Acquire 语义;也就是说,在读取指令序列中的易失性存储器之后发生的对全局或静态对象的引用将发生在编译的二进制文件中的易失性读取之后。

这使得易失性对象能够用于多线程应用程序中的内存锁定和释放。


对于除 ARM 以外的体系结构,如果未指定 /volatile 编译器选项,则编译器执行时就像指定了 /volatile:ms 一样;因此,对于 ARM 以外的体系结构,我们强烈建议您指定 /volatile:iso,并在处理跨线程共享的内存时使用显式同步原语和编译器内在函数。

微软提供编译器内在函数对于大多数 Interlocked* 函数,它们将编译为类似的内容LOCK XADD ...而不是函数调用。

直到“最近”,C/C++ 一般都不支持原子操作或线程,但这种情况在 C11/C++11 中发生了变化,添加了原子支持。使用<atomic>header 及其类型/函数/类将对齐和重新排序的责任移交给编译器,因此您不必担心这一点。您仍然必须对内存屏障做出选择,这决定了编译器生成的机器代码。通过放松的记忆顺序,load原子操作很可能最终会成为一个简单的MOVx86 上的指令。更严格的内存顺序可以添加栅栏,并且可能LOCK如果编译器确定目标平台需要它,则添加前缀。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多核CPU上32位读取的原子性 的相关文章

随机推荐

  • Laravel 5.2 中 auth()->user() 为 null

    我刚刚将 Composer 更新到 Laravel 5 2 无法查看受密码保护的页面 基本上下面的代码行不起作用 auth gt user 有人可以建议为什么这不起作用吗 确保任何需要会话 Auth 使用的 的路由都位于 web 中间件组
  • 使用应用内购买来解锁功能与使用 iPhone 的免费和付费应用版本

    我有一个应用程序 我打算将其作为具有部分全部功能的免费 精简版 版本和具有高级功能的付费完整版本发布 现在 通过在应用程序内购买免费应用程序 我正在考虑走这条路 能够根据需要解锁功能 我不是在谈论过期的试用版 基本上 我希望人们能够试用该应
  • 创建未知类型的数组

    我有一个对象 我必须验证该问题的值 对象的一些属性是自定义对象的数组 这样它将涉及到对数组的各个元素进行一些深入研究 为每个元素执行 getter 例如 AttribGrp x Object getAttribGrp x i getSome
  • 在.net中加载dll而不锁定它

    我正在执行一项任务 其中我必须加载 dll 并从中获取一些信息 例如类名等 但是当我将该 dll 加载到我的代码中时 它被锁定并且无法从源代码构建 直到我关闭加载程序 我尝试了某些解决方案 但没有一个适合我 Shadowcopy 在这种情况
  • 如何在 AngularJS 中动态更改 CSS 属性

    现在我有一个背景图像 URL 硬编码到 CSS 中 我想使用 AngularJS 中的逻辑动态选择背景图像 这是我目前拥有的 HTML div class offer detail image div div CSS offer detai
  • IPython Notebook 错误:加载笔记本时出错

    在过去的几分钟内 我无法访问任何 ipynb以前用Python3 4创建的文件 我进入包含这些文件的子目录 输入 ipython3 notebook 当我打开 新 笔记本时 出现以下错误 Unexpected error while sav
  • 服务层中的授权和用户信息(.NET 应用程序)

    我目前正在 NET 环境 n 层 中使用企业应用程序 我想知道在我的 BusinessLayer BL 中管理身份验证 授权 数据过滤的最佳方法 我们将从多个接口 ASP NET 应用程序和 Web 服务 使用该 BL 我认为我的 Serv
  • 如何使用 MouseListener 查找网格中的特定单元格

    我正在尝试创建一个由单元格组成的 10 x 10 网格的 Java 游戏 网格看起来像这样 public class Grid extends JPanel implements MouseListener public static fi
  • PHP:在mysql中的时间戳值内分割日期和时间

    我在数据库表中有一个名为 时间戳 的字段 它以以下格式存储值 YYYY MM DD HH MM SS 我想分开 然后获取变量中的日期 YYYY MM DD 以及另一个变量中的时间 HH MM SS 例子 timestamp 2012 10
  • 跨内存管理器边界传递 Delphi const 字符串参数是否安全?

    主题 我想使用字符串而不是 PChar 因为这样可以省去很多转换 但如果我这样做 procedure SomeExternalProc s string external SOMEDLL DLL 然后使用非共享内存管理器在其他一些项目中实现
  • 如何在 Github actions 中设置 Dockerfile ARG

    我有一个适用于我的 Node js 服务之一的 Dockerfile 我尝试使用 Github 操作将其推送到我的 Digitalocean 注册表 我的 Node js 服务需要一个由我自己在 npm js 注册表上托管的私有包 在我的
  • 在 javascript 或 bootstrap 中创建可点击的工具提示

    制作如下图所示的可点击工具提示的最佳方法是什么 我应该使用 bootstrap 还是其他库 Thanks 干得好 Pops popover html true content function return popover content
  • Qt如何捕获指示所有gui元素已准备就绪的事件

    我想知道是否有可能捕获所有时生成的事件 Qt 对象已初始化并准备就绪 看来有些事情不能在window的构造函数中完成 它们在插槽实现中工作得很好 例如 当我想访问应用程序的根窗口时 我会这样做 in h MainWindow rootWin
  • Jquery:ajax post 和编码

    我无法理解为什么我无法从服务器答案中获得正确的 ISO 8859 1 字符集 由于这是一项针对遗留代码的工作 我几乎无法更改页面上的字符集编码 我使用 JQuery 调用 post server side code t ctext i io
  • 通过 Spark 写入 HBase:任务不可序列化

    我正在尝试使用 Spark 1 0 在 HBase 0 96 0 hadoop2 中写入一些简单的数据 但我不断遇到序列化问题 这是相关代码 import org apache hadoop hbase client import org
  • HTML - 属性与属性[重复]

    这个问题在这里已经有答案了 Is idHTML 的属性或属性 我应该做什么 selector attr id or selector prop id 我读了很多文章 但仍然很困惑 有人可以用非常简单的语言向我解释 HTML JS 中的属性和
  • 在哪里可以找到面部检测软件、算法等? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 目前不接受答案 我对编写依赖于能够识别图片 或视频帧 中是否有面孔的软件感兴趣 它不需要识别人脸 因此除了以下指标之外没有其他指标 图片中是否有人脸 或不止一张 眼
  • Liquibase 命令行在 sql 中创建 diff 更改日志

    我实际上在 Windows 上的命令行中使用 Liquibase 并且尝试创建一个表示两个数据库之间差异的 sql 脚本 不幸的是我只得到 xml 文件作为回报 你能帮助我吗 我的命令行 liquidbase bat driver com
  • 在 Swift 中添加时间偏移

    我的数据库中有很多不同的显示时间 并且希望通过创建偏移量来根据用户时区显示正确的时间 我正在获取用户与 GMT 的时区偏移量 然后首先将其转换为小时 NSTimeZone localTimeZone secondsFromGMT 60 60
  • 多核CPU上32位读取的原子性

    注意 我根据我认为可能提供帮助的人在哪里添加了标签到这个问题 所以请不要喊 在我的 VS 2017 64 位项目中 我有一个 32 位长值m lClosed 当我想更新它时 我使用其中之一Interlocked函数族 考虑这段代码 在线程