调用线程会在thread.join()之后看到对局部变量的修改吗?

J. *_*rez 4 c++ concurrency memory-model thread-safety c++11

在最简单的示例中,假设我有一个启动线程的函数,该函数依次将局部变量的值设置为true。我们加入线程,然后离开函数。

bool func() {
    bool b = false;
    std::thread t([&]() { b = true; }); 
    t.join();
    return b;
}
Run Code Online (Sandbox Code Playgroud)

该函数将返回true,还是行为未定义?

Sto*_*ica 7

是的,它必须返回true。

[thread.thread.member]

void join();
Run Code Online (Sandbox Code Playgroud)

4 效果:阻塞直到由表示的线程*this完成。

5 同步:表示的线程的完成*this与([intro.multithread])相应的成功join()返回同步。

因此,由句柄表示的线程的执行以及相关的副作用是在join返回调用上下文之前完成的。

让我们看一下两个函数,它们的区别仅在于它们加入线程的时间不同:

int count_A() {
    int counter = 0;
    bool flag(true);
    auto t = std::thread([&]{flag = false;});

    while(flag) {    // infinite loop - flag never synchronized
        ++counter;
    }
    t.join();        // joins thread after loop exits
    return counter;
}
int count_B() {
    int counter = 0;
    bool flag(true);
    auto t = std::thread([&]{flag = false;});

    t.join();       // joins thread before loop, forcing synchronization
    while(flag) {
        ++counter;
    }
    return counter;
}
Run Code Online (Sandbox Code Playgroud)

-O3优化时使用g ++ 8.2版进行编译时,调用会count_A导致无限循环,因为编译器假定flag始终为true。

另一方面,调用count_B只会返回的值0。因为的值flag是在之后检查的thread.join(),所以它的值会重新加载,而flag是,false因此while循环不会执行。

请注意,如果flag将更改为atomic_bool,则count_A具有将计数器递增直到标志设置为false的预期行为,并且函数不会进入无限循环(而是flag子线程将返回一次设置为false)。