【问题标题】:C++ Multi-threading: Visible side-effects of non-atomic variablesC++ 多线程:非原子变量的可见副作用
【发布时间】:2020-04-18 00:23:37
【问题描述】:

C++ 标准中关于多线程内存模型的部分我不理解。

A 可见的副作用 A 在标量对象或位域 M 上相对于 M 的值计算 B 满足条件:

  • A 发生在 B和

    之前
  • X 对 M 没有其他副作用,即 A 发生在 X 之前,X 发生在 B 之前。

非原子标量对象或位域 M 的值,由评估 B 确定,应为可见副作用 A 存储的值。

而且根据 C++ 标准,线程之间的“发生在”关系必须通过“同步”或“是依赖顺序的”来建立before”,所以如果没有线程间同步,“happens before”关系就不会建立。

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

相反,T2 在 T2 启动时与主线程建立了“synchronizes with”关系,因此 T2 应该在主线程启动之前看到主线程设置的 M 的值线程,因为主线程和 T2 之间存在“发生之前”的关系。

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

【问题讨论】:

  • 我不会发布答案,因为我担心它会被审查,但标准不会以任何方式形成或定义 MT 程序的行为。您需要的直觉是:对变量访问使用互斥。

标签: c++ c++11 language-lawyer lock-free stdatomic


【解决方案1】:

T1 写入一个非原子变量 M,那么根据上面的引用,T2 应该永远不会看到 M 被 T1 修改

考虑以下几点:

如果

,两个动作可能是并发的
  • 它们由不同的线程执行,或者

  • 它们是无序的,至少有一个由信号处理程序执行,并且它们不是由同一个信号处理程序调用执行的。

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

如果其中一个修改了内存位置,而另一个读取或修改了相同的内存位置,则两个表达式计算会发生冲突。

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

最后,我们来:

如果程序的执行包含两个潜在的并发冲突操作,则程序的执行包含数据竞争,其中至少一个不是原子的,并且两者都不会在另一个之前发生,除了下面描述的信号处理程序的特殊情况。任何此类数据竞争都会导致未定义的行为。

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

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

【讨论】:

  • 感谢您的解释!非常清楚。因此,在这种情况下,我必须确保 T1 的写入“发生在”T2 的读取,或者 T2 的读取“发生在”T1 的写入。否则我有一个“数据竞争”,这是未定义的行为。
猜你喜欢
  • 2021-09-20
  • 1970-01-01
  • 2020-12-08
  • 2021-07-15
  • 1970-01-01
  • 2012-04-18
  • 2014-11-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多