Oli*_*ain 1 c++ multithreading memory-barriers
假设我的代码如下:
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();
}
Run Code Online (Sandbox Code Playgroud)
我保证WillRunInThread会打印 10 个吗?我已经看到一些 StackOverflow 答案表明这是安全的。但是,我不确定情况是否如此。具体来说,我认为存在以下两种可能性,这两种可能性都会产生问题:
Foo.x_可以在t_启动时存储在寄存器中。foo_将出现前被修建t_,这只是在同一个线程。例如,编译器可以自由地重新排序foo_.x_until after的初始化t_。我没有看到std::thread的构造函数作为任何类型的内存栅栏来防止上述两个问题的迹象。但是,我经常看到像上面这样的代码,这让我觉得我错过了一些东西。任何澄清将不胜感激。
从实现细节的角度考虑“这个变量可能存储在寄存器中”或“编译器可能想要重新排序这些指令”是错误的。坚持标准告诉你的。
线程启动时有一个同步点(C++17 [thread.thread.constr]/6):
构造函数调用的完成与 的副本调用的开始同步
f。
“A 与 B 同步”意味着在 A 之前(在 A 的线程中)完成的所有副作用将对 B 之后(在 B 的线程中)运行的所有代码可见。见 [intro.races]/9-12。
特别是,如果线程 1 使用std::thread构造函数启动一个线程,并且线程 2 是启动的线程并且它的初始函数是f,那么 内部的任何代码f,或从 调用的函数内部f,将看到由线程 1 引起的所有副作用在执行std::thread构造函数之前。
由于 的初始化foo_.x_在 的初始化之前进行t_,因此新线程将读取值 10。