假设我的代码如下:

struct Foo {
  Foo() : x(10) {}
  int x_;
}

void WillRunInThread(const Foo* f) {
  cout << "f.x_ is: " << f->x_ << endl;
}

struct Baz {
  Baz() : foo_(), t_(&WillRunInThread, &foo_) {}
  Foo foo_;
  std::thread t_;
}

int main(int argc, char** argv) {
  Baz b;
  b.t_.join();
}
我可以保证WillRunInThread可以打印10吗?我已经看到some StackOverflow answers表示这是安全的。但是,我不确定是这种情况。具体来说,我认为存在以下两种可能性,这两种情况都会造成问题:
Foo.x_启动时,可以将
  • t_存储在寄存器中。
  • 编译器可以重新排序某些指令。虽然C++保证foo_看起来像是在t_之前构造的,但仅在同一线程中构造。编译器可以自由地对foo_.x_的初始化进行重新排序,直到t_之后。

  • 我没有看到std::thread的构造函数充当防止上述两个问题的任何形式的内存隔离的迹象。但是,我经常看到类似上面的代码,这让我觉得我缺少了一些东西。任何澄清将不胜感激。

    最佳答案

    从实现细节方面进行思考是错误的,例如“此变量可能存储在寄存器中”或“编译器可能希望对这些指令进行重新排序”。遵守标准告诉您的内容。
    线程启动时会出现一个同步点(C++ 17 [thread.thread.constr]/6):

    “A与B同步”表示在A(在A的线程中)之前在A(在A的线程中)完成的所有副作用都是可见的。参见[介绍种族]/9-12。
    特别是,如果线程1使用std::thread构造函数启动线程,并且线程2是启动的线程,并且其初始函数是f,则f内或从f调用的函数内的任何代码都将看到所有的副作用在执行std::thread构造函数之前,由线程1引起的。
    由于foo_.x_的初始化在t_的初始化之前进行了排序,因此新线程将读取10的值。

    关于c++ - 安全使用线程中先前构造的对象,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/63122031/

    10-11 22:43
    查看更多