【问题标题】:can someone trace the reason for this race condition?有人可以追踪这种比赛条件的原因吗?
【发布时间】:2023-03-20 14:33:02
【问题描述】:

c++ atomics 竞争条件 我正在学习 c++ atomics。所以我故意制造了一个竞争条件。你可以看到我的 void add 函数将 x 增加 1 打印 x 的值和增加它的线程名称。两个线程 t1 和 t2 可以访问 void add 函数。所以这是一个竞争条件,我得到的输出类似于 1,1 1,2(最佳情况)2,2 和 2,1。我的问题是 2,1 输出是如何可能的。请任何人帮忙。

#include <iostream>
#include<thread>
#include<atomic>


using namespace std;

int x = 0;
std::atomic<int>s{ 0 };

void add(const char* c) {
    ++x;
    cout<< c << endl << x << endl;
}

int main() {
    std::thread t1(add,"t1");
    std::thread t2(add,"t2");
    t1.join();
    t2.join();
    cin.get();
}

【问题讨论】:

  • 有一个常见的误解,认为“原子”意味着“线程安全,我可以在任何线程中以任何我想要的方式访问这个变量,我总是会得到我认为正确的结果”。这不是真的。 P.S.:显示的代码甚至没有使用原子变量,它完全没有使用。
  • @RichardCritten:这不是真的,例如任何时候你的进程的输出取决于它的一些线程的时间你有一个竞争条件。这并不意味着相应的 C++ 程序会调用未定义的行为。竞争条件和数据竞争不是一回事。一个例子:如果我启动两个线程都打印它们的 ID,我创建了一个竞争条件,但既不是数据竞争也不是未定义的行为。
  • @Peter 请参阅上面 cppreference 的报价 - 我会寻找标准报价。
  • 您的问题混淆了两个不相关的问题。首先,编写的代码表现出未定义的行为,通过在两个没有同步的线程中更新非原子对象x。任何事情都允许发生。其次,即使将x 替换为s 从而避免未定义的行为,程序也是不确定的:线程t1 可以在t2 启动之前完成,或者t2 可以在t1 启动之前完成,或者两者可以交错(例如,一个线程执行增量,然后另一个执行增量,然后它们打印相同的值2)。
  • 根据 C++ 标准,是的,这是未定义的行为。在实践中,程序行为可能由使用的实现定义(仅由非原子读/写)。问题是你没有同步std::cout的使用,因此&lt;&lt;的效果可能会以任何方式交错。

标签: c++ atomic race-condition


【解决方案1】:

您的问题混淆了两个不相关的问题。首先,编写的代码表现出未定义的行为,通过在两个没有同步的线程中更新非原子对象x。任何事情都可以发生;试图对特定结果进行推理是没有意义的。

其次,即使将x 替换为s 从而避免未定义的行为,程序也是不确定的:线程t1 可以在t2 启动之前完成,或者t2 可以在t1 之前完成开始,或者两者可能交错(例如,一个线程执行增量,然后另一个执行增量,然后它们打印相同的值2)。该程序可以打印t1 1 t2 2t2 1 t1 2t1 2 t2 2 甚至t1 t2 2 1 - 所有这些结果都与程序定义的非确定性抽象机器的一种可能执行一致。

【讨论】:

    猜你喜欢
    • 2021-06-10
    • 1970-01-01
    • 1970-01-01
    • 2012-08-29
    • 2011-12-25
    • 1970-01-01
    • 1970-01-01
    • 2015-10-20
    • 1970-01-01
    相关资源
    最近更新 更多