【问题标题】:Does the C++11 memory model prevent memory tearing and conflicts?C++11 内存模型能防止内存撕裂和冲突吗?
【发布时间】:2012-06-05 12:13:11
【问题描述】:

阅读 C++11 的草稿我对第 1.7.3 条感兴趣:

一个内存位置要么是一个标量类型的对象,要么是一个最大的相邻位域序列,它们都具有非零宽度。 ... 两个执行线程 (1.10) 可以更新和访问单独的内存位置,而不会相互干扰。

此子句是否保护与硬件相关的竞争条件,例如:

  • 在两个总线事务中更新内存的未对齐数据访问(内存撕裂)?
  • 系统内存单元中有不同的对象,例如一个 32 位字中的两个 16 位有符号整数,并且单独对象的每次独立更新都需要写入整个内存单元(内存冲突)?

【问题讨论】:

  • @HansPassant:从我的快速阅读来看,这个问题似乎与线程之间共享对象的可见性更相关,我在这里询问由更新不同对象引起的内存冲突。

标签: c++ multithreading thread-safety c++11


【解决方案1】:

关于第二点,标准保证那里不会有比赛。话虽如此,但有人告诉我,目前的编译器没有实现这种保证,在某些架构中甚至可能无法实现。

关于第一点,如果第二点得到保证,并且如果您的程序不包含任何竞态条件,那么自然的结果就是这也不会是竞态条件。也就是说,假设写入不同子词位置的标准 保证 是安全的,那么唯一可以有竞争条件的情况是多个线程访问同一个变量(即拆分跨单词,或者更可能因为这有问题,跨缓存行)。

同样,这可能很难甚至不可能实现。如果您的未对齐数据跨越缓存行,那么几乎不可能保证代码的正确性而不会对性能造成巨大成本。由于这个原因和其他原因(包括原始性能,对涉及两个缓存行的对象的写入涉及将多达 32 个字节写入内存,并且如果任何其他线程正在触及任何一个缓存行,它还涉及缓存同步的成本......

【讨论】:

    【解决方案2】:

    它不能防止内存撕裂,这仅在两个线程访问同一内存位置时可见(但该子句仅适用于不同的内存位置)。

    根据您的示例,它似乎可以防止内存冲突。实现这一点的最可能方法是一次不能写入少于 32 位的系统将具有 32 位 char,然后两个单独的对象将永远无法共享“系统内存单元”。 (在具有 32 位 char 的系统上,两个 16 位整数可以相邻的唯一方法是作为位域。)

    【讨论】:

    • 这个问题对于多线程来说有点复杂,而不仅仅是 8/32 字节。 L1 高速缓存的实际写入是通过高速缓存行大小粒度完成的。当前的编译器通常不能在一个块中写入少于 X(16 字节是英特尔的常用数字)字节。另一方面,处理器使用其他技术来保证(或尝试)这不是问题,例如将缓存行标记为脏并强制其他处理器在写入之前重新加载或更复杂的同步算法。
    • @dribeas:在 ISA 级别,可以更新较小的区域。处理器必须采取总线锁,或使用高速缓存一致性算法来实现这一点是一个实现细节。重要的是,ISA 为较小区域的原子更新提供了原语,而无需软件互斥体。
    • 是的……也不是。未对齐访问的问题不在于从处理器到缓存的写入大小,而在于写入实际上可能跨越缓存边界这一事实。虽然写入单个缓存行是由缓存一致性算法处理的,但我不确定这些算法是否可以处理模拟写入不同缓存行的原子性(即硬件必须锁定两个缓存行,写入和释放;或者标记两者行一次脏或...无论算法做什么,但同时在两条行上)
    • @dribeas:已经同意不会阻止撕裂。提供的是更新不会更改不属于对象的字节(撤消另一个线程所做的更改)。对两个缓存行的两个非事务性但单独的原子更新提供了这一点。
    猜你喜欢
    • 1970-01-01
    • 2012-08-25
    • 2015-01-31
    • 1970-01-01
    • 2020-10-10
    • 2023-03-12
    • 2013-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多