是否应该立即加载获取商店版本?

时间:2015-06-11 11:11:53

标签: c++ multithreading atomic atomicity

假设我们有一个简单的变量(std::atomic<int> var)和2个线程T1T2,我们有T1的以下代码:

...
var.store(2, mem_order);
...

T2

...
var.load(mem_order)
...

另外,我们假设T2(加载)稍后执行123ns (稍后在C ++标准的修改顺序中),而不是T1(存储)。 我对这种情况的理解如下(针对不同的记忆顺序):

  1. memory_order_seq_cst - T2加载必须加载2。因此,它必须有效地加载最新值(就像RMW操作的情况一样)
  2. memory_order_acquire / memory_order_release / memory_order_relaxed - T2没有义务加载2,但可以使用唯一限制加载任何旧值:该值应该不比该线程加载的最新版本旧。因此,例如var.load会返回0
  3. 我的理解是对的吗?

    UPDATE1:

    如果我的推理错了,请提供C ++标准中的文本证明。不仅仅是对某些架构如何运作的理论推理。

3 个答案:

答案 0 :(得分:4)

  

我的理解是对的吗?

没有。你误解了记忆顺序。

  

让我们假设T2(加载)比T1(商店)执行晚了123 ... ...

在这种情况下,T2将看到T1对任何类型的内存顺序所做的事情(此外,此属性适用于任何内存区域的读/写,参见例如http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4431.pdf,1.10,第15页)。您的短语中的关键词是以后:这意味着其他人会强制对这些操作进行排序。

内存订单用于其他场景:

让一些操作OP1在存储操作之前出现在线程T1中,OP2出现在它之后,OP3在加载操作之前进入线程T2,{ {1}}来到它之后。

OP4

假设线程可以观察到//T1: //T2: OP1 OP3 var.store(2, mem_order) var.load(mem_order) OP2 OP4 var.store()之间的某些顺序。什么人可以保证跨线程其他操作的顺序

  1. 如果var.load()使用var.store,则memory_order_release使用var.load,而memory_order_acquire则会在 var.store之前订购(是,加载返回2),然后var.load的效果在 OP1之前排序
  2. ,如果OP4写了一些变量var1,OP1读取该变量,那么可以确保OP4将读取之前写的OP4。这是最常用的案例。

    1. 如果OP1var.store同时使用var.loadmemory_order_seq_cst var.store后排序(即加载返回0) ,这是存储前变量的值),然后var.load的效果在 OP2后排序
    2. 某些棘手的同步方案需要此内存顺序。

      1. 如果OP3var.store使用var.load,那么对于memory_order_relaxedvar.store的任何订单,都可以保证无订单跨线程操作。
      2. 其他人确保操作顺序时,使用此内存顺序。例如,如果在var.load T2之后创建线程var.store,则在T1之后订购OP3OP4

        更新OP1隐含123 ns later,因为计算机的处理器没有关于通用时间的概念,并且没有操作精确时刻什么时候执行。对于两次操作之间的测量时间,您应该:

        1. 某些cpu 上完成第一个操作和开始时间计数操作之间的顺序。
        2. 观察开始和结束时间计数操作之间的顺序。
        3. 观察完成时间计数操作和第二次操作开始之间的排序。
        4. 传递上,这些步骤在第一个操作和第二个操作之间进行排序。

答案 1 :(得分:1)

没有发现任何证据证明我的理解错误,我认为这是正确的,我的证据如下:

  

memory_order_seq_cst - T2负载必须加载2.

这是正确的,因为使用memory_order_seq_cst的所有操作应该在所有内存操作的原子变量上形成单个总顺序。 摘自标准:

  

[29.9 / 3] 所有memory_order_seq_cst上应该有一个总订单S   操作,符合“之前发生”的顺序​​和   所有受影响位置的修改订单,例如每个   memory_order_seq_cst操作B从原子加载值   对象M观察以下值之一&lt; ...&gt;

我的问题的下一点:

  

memory_order_acquire / memory_order_release / memory_order_relaxed - T2是   没有义务加载2但可以加载任何旧值&lt; ...&gt;

我没有找到任何可能表明稍后在修改订单中执行的加载应该看到最新值的证据。我发现存储/加载操作的唯一要点是任何与memory_order_seq_cst不同的内存顺序是:

  

[29.3 / 12] 实现应该使原子存储对原子可见   在合理的时间内装载。

  

[1.10 / 28] 实现应确保原子或同步操作分配的最后一个值(按修改顺序)   将在有限的时间内对所有其他线程可见。

因此,我们唯一的保证是写入的变量将在一段时间内可见 - 这是非常合理的保证,但并不意味着前一个商店的即时可见性。这证明了我的第二点。

鉴于我最初的理解是正确的。

答案 2 :(得分:0)

123 ns 之后不会强制对 T2 进行排序以查看 T1 的结果。那是因为如果运行 T2 的物理程序计数器(晶体管等)与运行 T1 的物理程序计数器(大型多核超级计算机等)相距 40 米以上,那么光速将不允许 T1 写入状态信息传播那么远(还)。如果用于加载/存储的物理内存与两个线程处理器相距一定距离,则效果类似。