C++ 17 atomics和condition_variable死锁

use*_*793 8 c++ multithreading atomic condition-variable c++17

我有以下代码,注释行上的死锁.基本上f1和f2作为程序中的单个线程运行.f1期望i为1并递减它,通知cv.f2期望i为0并递增它,通知cv.我假设如果f2将i增加到1,则调用死锁,调用cv.notify(),然后f1读取过时的i值(为0),因为互斥锁和i之间没有内存同步,然后等待并且永远不会被唤醒起来.然后f2也进入睡眠状态,现在两个线程都在等待一个永远不会被通知的cv.

如何编写此代码以便不会发生死锁?基本上我想要实现的是拥有一些由两个线程更新的原子状态.如果其中一个线程的状态不正确,我不想旋转; 相反,我想使用cv功能(或类似的东西)在值正确时唤醒线程.

我使用g ++ - 7用O3编译代码(尽管在O0和O3中都发生了死锁).

#include <atomic>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>

std::atomic_size_t i{0};
std::mutex mut;
std::condition_variable cv;

void f1() {
  while (1) {
    {
      std::unique_lock<std::mutex> lk(mut);
      cv.wait(lk, []() { return i.load() > 0; }); // deadlocks
    }
    --i;
    cv.notify_one();
    std::cout << "i = " << i << std::endl; // Only to avoid optimization
  }
}

void f2() {
  while (1) {
    {
      std::unique_lock<std::mutex> lk(mut);
      cv.wait(lk, []() { return i.load() < 1; }); // deadlocks
    }
    ++i;
    cv.notify_one();
    std::cout << "i = " << i << std::endl; // Only to avoid optimization
  }
}

int main() {
  std::thread t1(f1);
  std::thread t2(f2);
  t1.join();
  t2.join();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

编辑:cout只是为了避免编译器优化.

use*_*670 8

我认为问题是i可以改变的值,可以notify_one在另一个线程评估后的间隔中调用,return i.load() > 0;但在lambda调用返回之前,cv恢复等待.这样,另一个线程将不会观察到原子变量的变化,也没有人将其唤醒以再次检查.这可以通过在更改变量时锁定互斥量来解决,但这样做会破坏原子的目的.