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

问题描述 投票:2回答:1

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

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

这个函数会返回true,还是行为未定义?

c++ c++11 concurrency thread-safety memory-model
1个回答
3
投票

是的,它必须返回true。

[thread.thread.member]

void join();

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

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

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

Example

让我们看看两个函数,它们只在加入一个线程时有所不同:

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;
}

当在-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时返回)。