了解为什么只有一个线程执行写操作时会发生竞争条件

时间:2015-03-03 08:16:27

标签: exception c++11 race-condition raii stdatomic

我最近问过" 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。你能描述一下这个案子吗?

1 个答案:

答案 0 :(得分:1)

标准说如果多个线程访问变量并且至少有一个访问是写入,那么如果变量不是原子的并且操作没有排序,那么就存在数据竞争,并且具有数据竞争的程序具有未定义的行为。

了解这些规则后,编译器可以假设非原子变量没有按顺序修改(因为否则任何程序是对源代码的有效解释);在你的示例代码中,这意味着编译器可以简单地假设alive_永远不会在busy循环中发生变化 - 虽然顺便说一下,像这样的非终止循环本身是未定义的行为。