0

我最近问“抛出的对象不能在多线程解决方案中被捕获”并得到了完美的正确答案。但是,我仍然很困惑为什么只有一个线程执行写操作时会出现竞争条件。让我粘贴原始有问题的代码:

#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

根据链接的问题,如果我改为使用 member 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(不需要,但至少不是“未定义行为”):

  • 变量solution被初始化:
    • solution_using_thread的默认构造函数在主线程中设置alive_为。true
    • 线程启动并且 的值alive_恰好false在第二个线程中。所以线程执行立即结束。
    • 在构造函数返回之前,第二个线程已经启动。
  • 我们throw 1
    • thread.join()立即返回,因为线程已经完成。
  • 输出是0 1 2

显然,至少还有一种情况,它只打印0. 你能描述一下那个案例吗?

4

1 回答 1

1

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

了解这些规则后,编译器可以假设非原子变量没有被乱序修改(否则任何程序都是对源代码的有效解释);在您的示例代码中,这意味着编译器可以简单地假设alive_在繁忙循环内永远不会改变——尽管顺便说一下,像这样的非终止循环本身就是未定义的行为。

于 2015-03-03T08:31:18.927 回答