我最近问过" Thrown object cannot be caught in a multi-threaded solution"并得到了完美的答案。但是,我仍然感到困惑,为什么只有一个线程执行写操作时会出现竞争条件。让我粘贴原始有问题的代码:
#include <iostream>
#include <thread>
using namespace std;
struct solution_using_thread {
solution_using_thread()
: alive_(true), thread_() {
thread_ = thread([this]() {
while(alive_);
});
}
~solution_using_thread() {
alive_ = false;
thread_.join();
}
private:
bool alive_;
thread thread_;
};
int main() {
cout << 0 << endl;
try {
solution_using_thread solution;
throw 1;
} catch (int i ) {
cout << i << endl;
}
cout << 2 << endl;
}
有时输出只是
0
根据链接的问题,如果我改为使用成员atomic<bool> alive_
,则输出将按预期进行
0
1
2
现在,我试图解释成员bool alive_
导致未定义行为的原因。
案例1(快乐结局):
solution
已初始化:
solution_using_thread
的主要帖子中的默认构造函数集alive_
为true
。alive_
的值恰好是第二个线程中的true
。因此线程执行停留在while
循环中。throw 1
。
solution
的析构函数。主线程中alive_
的值为true
。thread.join()
阻塞,直到alive_
的值与第二个线程同步。alive_
同步后,while循环终止,第二个线程结束,thread_.join()
返回,堆栈展开很快就完成了。0 1 2
案例2(不需要,但至少没有&#39;未定义的行为&#39;):
solution
已初始化:
solution_using_thread
的主要帖子中的默认构造函数集alive_
为true
。alive_
的值恰好是第二个线程中的false
。所以线程执行立即结束。throw 1
thread.join()
立即返回,因为该线程已经完成。0 1 2
显然,至少还有一个案例,它只打印0
。你能描述一下这个案子吗?
答案 0 :(得分:1)
标准说如果多个线程访问变量并且至少有一个访问是写入,那么如果变量不是原子的并且操作没有排序,那么就存在数据竞争,并且具有数据竞争的程序具有未定义的行为。
了解这些规则后,编译器可以假设非原子变量没有按顺序修改(因为否则任何程序是对源代码的有效解释);在你的示例代码中,这意味着编译器可以简单地假设alive_
永远不会在busy循环中发生变化 - 虽然顺便说一下,像这样的非终止循环本身是未定义的行为。