假设我的代码如下:
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_
存储在寄存器中。 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/