【问题标题】:Possible data race between writes to the same address in memory写入内存中相同地址之间可能存在数据竞争
【发布时间】:2017-12-20 11:30:03
【问题描述】:

考虑以下 sn-p。

T data;
T* ptr1 = &data;
T* ptr2 = &data;
*ptr1 = ...;
std::thread thread([ptr2]() {
    *ptr2 = ...;
});
thread.join();

问题:它会引入数据竞争吗?

我的想法:由于ptr1ptr2 是不同的变量,因此赋值和按值捕获需要重新排序。因此,存在数据竞赛。

这是正确的吗?

【问题讨论】:

  • 怎么可能?线程在第一次修改后生成。该执行仍在按顺序执行。
  • @StoryTeller 啊哈,这就是我错过的!产生一个线程至少会引入一个内存栅栏。对吗?
  • "[线程的]构造函数调用的完成与(定义在std::memory_order中)在新的f的副本的调用开始同步执行线程。”来自这里:en.cppreference.com/w/cpp/thread/thread/thread
  • @RichardCritten 但这不仅仅意味着线程执行的开始发生在构造 lambda 之后。而且由于第 4 行的赋值和 lambda 的构造函数是未排序的(或者我在这里错过了什么?),这并不能回答我的问题。
  • @SolenodonParadoxus - 它实际上比这更基本。 *ptr1 = ...;thread 的声明是 two full expressions sequenced in a particular order。根据抽象机器本身,一个必须在另一个之前完全发生。编译器无法真正进行会改变这一点的转换。

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


【解决方案1】:

线程创建意味着发生在关系之前,即给定代码中没有数据竞争。事实上,对*ptr1 的赋值实际上发生在下一条语句之前。如果您在创建线程后重新排序代码以分配给*ptr1,您将遇到数据竞争:使用哪个指针引用来访问对象以创建数据竞争并不重要。

【讨论】:

  • 您能否详细说明“事实上,对 *ptr1 的赋值实际上发生在下一条语句之前”?你到底是什么意思?
  • 在一个线程中,操作在每个完整表达式的末尾进行排序。即使编译器/CPU 可以重新排序操作,只有在程序的语义没有改变的情况下才允许这样做。引入数据竞赛就是这样一种语义变化。
【解决方案2】:

我认为这里不会有比赛。在您创建新线程之前,程序运行单线程,因此执行顺序将由您编写。

Here's关于编译时内存排序的解释。

还有来自这个页面的一个很好的引用:

The cardinal rule of memory reordering, which is universally followed by compiler developers and CPU vendors, could be phrased as follows: Thou shalt not modify the behavior of a single-threaded program.

【讨论】:

    猜你喜欢
    • 2014-08-26
    • 1970-01-01
    • 2016-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多