std::atomic 的读取线程安全吗?

Zha*_*ang 4 c++ multithreading c++20

我的目的——只有一个线程可以同时处理数据。我使用过 std::mutex、std::unique_lock,try_lock效果很好。

但有人争论,为什么不直接使用原子变量呢?像这样的事情,

#include <atomic>
#include <thread>

std::atomic_bool isRunning = false;

void process() {    
    if (isRunning)
    {
        return;
    }
    isRunning = true;
    cout << "processing ..." << endl;
    std::this_thread::sleep_for(std::chrono::seconds(5));
    cout << "finished" << endl;
    isRunning = false;
}

int main(int argc, wchar_t* argv[])
{
    std::thread th1(process);
    std::thread th2(process);
    std::thread th3(process);
    std::thread th4(process);
    std::thread th5(process);

    th1.join();
    th2.join();
    th3.join();
    th4.join();
    th5.join();
}
Run Code Online (Sandbox Code Playgroud)

根据我的经验,atomic 对于写入来说是安全的,但对于读取来说并不安全。例如,if (isRunning)可能有 2 个线程false同时获得相同的值,并且都运行到处理代码中。

但我测试了好几次,都没有这种情况,都只显示一条信息

加工 ...

我没有找到任何相关文档,您对此有何看法?

Jer*_*ner 9

发布的代码是线程安全的,因为它不会调用未定义的行为,但不是线程安全的,因为它不会为您提供您试图保证的行为。

特别是,此代码中存在竞争条件:

if (isRunning)
{
    return;
}
// XXX
isRunning = true;
Run Code Online (Sandbox Code Playgroud)

想象一下这样的场景:线程 A 执行,并到达上面显示 XXX 注释的行...然后操作系统的调度程序抢占线程 A 并允许线程 B 执行,线程 B 设置为并isRunning开始true执行其通常的处理。然后线程 A 回到 CPU,(再次)设置isRunningtrue,并开始执行其处理。

此时,线程 A 和线程 B 同时处理数据,这是您试图避免的情况。

结果是:为此目的,互斥锁是完成这项工作的正确工具;原子变量有其用途,但它不会让您(有效地)阻止一个线程的进度,直到另一个线程完成执行关键部分,而这正是您在此程序中想要的行为。