C++ 多线程:非原子变量的可见副作用

C++ Multi-threading: Visible side-effects of non-atomic variables

C++标准中有一部分关于多线程内存模型我不明白。

A visible side effect A on a scalar object or bit-field M with respect to a value computation B of M satisfies the conditions:

  • A happens before B and

  • there is no other side effect X to M such that A happens before X and X happens before B.

The value of a non-atomic scalar object or bit-field M, as determined by evaluation B, shall be the value stored by the visible side effect A.

而且根据 C++ 标准,线程之间的“发生在”关系必须通过“同步”或“之前是依赖排序的”,因此如果没有线程间同步,“发生在”关系将不会建立。

现在假设有两个线程 T1 和 T2,它们都由主线程启动并且彼此之间从不进行任何同步(因此不会有任何“发生在之前” T1 和 T2 之间建立的关系)。如果 T1 写入一个 non-atomic 变量 M,那么根据上面的引用,T2 永远不会看到 M 被 T1 修改,因为没有“发生在" T1 和 T2 之间的关系。

相反,T2 在 T2 启动时与主线程建立了“同步”关系,因此 T2 应该先看到主线程设置的 M 值由主线程启动,因为主线程和T2之间存在“happens before”关系。

对吧?但是我在我的机器上做了一个实验,事实并非如此。怎么了?

T1 writes to a non-atomic variable M, then according to the quote above, T2 should never see M modified by T1

考虑以下因素:

Two actions are potentially concurrent if

  • they are performed by different threads, or

  • they are unsequenced, at least one is performed by a signal handler, and they are not both performed by the same signal handler invocation.

T2 对 M 的读取和 T1 对 M 的写入是“潜在并发的”。下一篇:

Two expression evaluations conflict if one of them modifies a memory location and the other one reads or modifies the same memory location.

T2 对 M 的读取与 T1 对 M 的写入冲突。因此这些“潜在并发”操作发生冲突。

最后,我们来到:

The execution of a program contains a data race if it contains two potentially concurrent conflicting actions, at least one of which is not atomic, and neither happens before the other, except for the special case for signal handlers described below. Any such data race results in undefined behavior.

T2 读取 M 不会发生在 T1 写入 M 之前,T1 写入 M 也不会发生在 T2 读取 M 之前。因此,您有数据竞争。

并且数据竞争是未定义的行为。并不是说 T2 看不到对 M 的写入;它可以看到任何东西:旧值、新值、某些第三值、发出的鼻腔守护进程,任何东西