C++ 多个条件变量:线程不同步问题

C++ 多个条件变量:线程不同步问题,c++,multithreading,C++,Multithreading,我有一个线程正在做“工作”,当条件变量通知它时,它应该报告进度。此线程正在等待条件变量 另一个线程等待x毫秒,然后通知条件变量继续 我有5个条件变量(这是学校的一个练习),一旦每个变量都得到通知,就应该报告工作进度: 我遇到的问题是,线程2(应该通知线程1的线程)通过所有5个检查点,最后只通知一次。因此,我最终会遇到这样一种情况,即最终进度为20%,线程1正在等待另一个通知,而线程2已经完成了所有通知 我的逻辑实现中的缺陷在哪里? 代码如下: #include <condition_var

我有一个线程正在做“工作”,当条件变量通知它时,它应该报告进度。此线程正在等待条件变量

另一个线程等待x毫秒,然后通知条件变量继续

我有5个条件变量(这是学校的一个练习),一旦每个变量都得到通知,就应该报告工作进度:

我遇到的问题是,线程2(应该通知线程1的线程)通过所有5个检查点,最后只通知一次。因此,我最终会遇到这样一种情况,即最终进度为20%,线程1正在等待另一个通知,而线程2已经完成了所有通知

我的逻辑实现中的缺陷在哪里? 代码如下:

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

using namespace std;

class Program {
public:
  Program() {
    m_progress = 0;
    m_check = false;
  }

  bool isWorkReady() { return m_check; }

  void loopWork() {
    cout << "Working ... : " << endl;

    work(m_cv1);
    work(m_cv2);
    work(m_cv3);
    work(m_cv4);
    work(m_cv5);

    cout << "\nFinished!" << endl;
  }

  void work(condition_variable &cv) {
    unique_lock<mutex> mlock(m_mutex);
    cv.wait(mlock, bind(&Program::isWorkReady, this));
    m_progress++;
    cout << " ... " << m_progress * 20 << "%" << endl;
    m_check = false;
  }

  void checkPoint(condition_variable &cv) {
    lock_guard<mutex> guard(m_mutex);
    cout << " < Checking >" << m_progress << endl;
    this_thread::sleep_for(chrono::milliseconds(300));
    m_check = true;
    cv.notify_one();
  }

  void loopCheckPoints() {
    checkPoint(m_cv1);
    checkPoint(m_cv2);
    checkPoint(m_cv3);
    checkPoint(m_cv4);
    checkPoint(m_cv5);
  }

private:
  mutex m_mutex;
  condition_variable m_cv1, m_cv2, m_cv3, m_cv4, m_cv5;
  int m_progress;
  bool m_check;
};

int main() {
  Program program;

  thread t1(&Program::loopWork, &program);
  thread t2(&Program::loopCheckPoints, &program);

  t1.join();
  t2.join();

  return 0;
}
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
班级计划{
公众:
程序(){
m_进度=0;
m_check=假;
}
bool isWorkReady(){返回m_check;}
无效循环工作(){
库特
我的逻辑实现中的缺陷在哪里

cv.notify_one
不要求
cv.wait(mlock,bind(&Program::isWorkReady,this));
之后的代码立即继续,因此在
cv.wait
之后的代码继续之前执行多个
检查点是完全有效的

但是在您执行
cv.wait
之后,您将
m\u check=false;
设置为
false
,因此如果没有进一步的
checkPoint
执行剩余,那么将设置
m\u check=true;
,您的
工作
功能将被卡住

您可以考虑将其设置为计数器,而不是
mu check
作为
bool
,它在
checkPoint
中递增,在
work
中递减
loopCheckPoints()
线程持有锁一段时间,设置
mu-check
然后释放锁并立即继续再次抓取锁。
loopWork()
线程可能没有在这段时间内醒来以对
mu-check
更改作出反应

  • 不要长时间持有锁。要尽快。如果不添加
    sleep
    s程序就无法运行,那么您就有问题了
解决此问题的一种方法是检查工作人员是否已将
m_check
设置回
false

    void work(condition_variable& cv) {
        { // lock scope
            unique_lock<mutex> mlock(m_mutex);
            cv.wait(mlock, [this] { return m_check; });
            m_progress++;
            cout << " ... " << m_progress * 20 << "%" << endl;
            m_check = false;
        }
        // there's no need to hold the lock when notifying 
        cv.notify_one(); // notify that we set it back to false
    }
无效工作(条件变量和cv){
{//锁定范围
唯一锁定锁定(m_互斥);
等待(mlock,[this]{返回m_check;});
m_progress++;

cout.非常感谢!我对条件变量有了更好的理解,而且我以前从未使用过额外的花括号来创建作用域!这似乎很好,至少在这种情况下是这样。@Jeekim,不客气!我很少使用这样的花括号,通常创建一个函数来提供作用域-但是在处理ve时我倾向于认为它更容易阅读、理解和维护。线程足够复杂:-)谢谢你的回复。我现在明白了为什么它不能像我想要的那样工作。我最终实现了Ted建议的解决方案,它感觉更直观-使用m_检查在线程之间切换
    void checkPoint(condition_variable& cv) {
        // if you are going to sleep, do it without holding the lock
        // this_thread::sleep_for(chrono::milliseconds(300));

        { // lock scope
            lock_guard<mutex> guard(m_mutex);
            cout << "<Checking> " << m_progress << endl;
            m_check = true;
        }
        cv.notify_one(); // no need to hold the lock here
        {
            // Check that m_check is set back to false
            unique_lock<mutex> mlock(m_mutex);
            cv.wait(mlock, [this] { return not m_check; });
        }
    }