C ++ 17原子和condition_variable死锁 [英] C++17 atomics and condition_variable deadlock

查看:369
本文介绍了C ++ 17原子和condition_variable死锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码,该代码在注释行上陷入僵局.基本上,f1和f2在程序中作为单独的线程运行. f1期望i为1并递减,通知简历. f2期望i为0并将其递增,从而通知cv.我假设如果f2将i增至1并调用cv.notify(),则会发生死锁,然后f1会读取i的陈旧值(即0),因为互斥体和i之间没有内存同步,然后等待并且永远不会被唤醒向上.然后f2也进入睡眠状态,现在两个线程都在等待永远不会被通知的cv.

I have the following code, which deadlocks on the commented lines. Basically f1 and f2 run as individual threads in the program. f1 expects i to be 1 and decrements it, notifying the cv. f2 expects i to be 0 and increments it, notifying the cv. I assume the deadlock occurs if f2 increments i to 1, calls cv.notify(), then f1 reads a stale value of i (which is 0) because there is no memory synchronization between the mutex and i and then waits and never gets woken up. Then f2 also enters a sleep state and now both threads are waiting on a cv that will never be notified.

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

How can I write this code so that the deadlock does not occur? Basically what I want to be able to achieve is having some atomic state that gets updated by two threads. If the state is not correct in one of the threads, I do not want to spin; rather I want to use the cv functionality (or something similar) to wake the thread up when the value is correct.

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

I am using g++-7 to compile the code with O3 (although the deadlock occurs in both O0 and 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;
}

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

cout is only to avoid compiler optimization.

推荐答案

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

I think the problem is that the value of i could be altered and notify_one could be called in the interval after another thread evaluated return i.load() > 0; but before lambda call returns and cv resumes wait. This way the change of atomic variable won't be observed by another thread and there is no one to wake it up to check again. This could be solved by locking mutex when changing variable though doing so would defeat the purpose of atomic.

这篇关于C ++ 17原子和condition_variable死锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆