C++ 为什么条件_变量::notify_all不能唤醒任何线程?

C++ 为什么条件_变量::notify_all不能唤醒任何线程?,c++,qt,c++11,condition-variable,unique-lock,C++,Qt,C++11,Condition Variable,Unique Lock,我使用conditional\u变量::notify\u all()唤醒等待的线程(只有一个线程在等待唯一的\u锁) 此代码段在大多数情况下工作正常,但日志文件(有关详细信息,请参阅下文)表明,在新创建的线程allready返回后,父线程无法获得唯一\u锁 如果能在这个问题上得到一些帮助,我将不胜感激 以下是相关的代码片段: void MainWindow::deployAction(void) { std::condition_variable cvRunOver; std::

我使用
conditional\u变量::notify\u all()
唤醒等待的线程(只有一个线程在等待
唯一的\u锁

此代码段在大多数情况下工作正常,但日志文件(有关详细信息,请参阅下文)表明,在新创建的线程allready返回后,父线程无法获得
唯一\u锁

如果能在这个问题上得到一些帮助,我将不胜感激

以下是相关的代码片段:

void MainWindow::deployAction(void)
{
    std::condition_variable cvRunOver;
    std::mutex mtxRunOver;
    std::unique_lock <std::mutex> ulkRunOver(mtxRunOver);
    QString workerThreadRes;
    std::thread workThread([&]()
    {
        workThread.detach();

        do_some_process_for_seconds();
        
        cvRunOver.notify_all();
        LOG(INFO)<<"to leave the subthread";
        google::FlushLogFiles(google::GLOG_INFO);
        return;
    });

    while (cvRunOver.wait_for(ulkRunOver, std::chrono::milliseconds(100)) == std::cv_status::timeout)
    {
        qApp->processEvents();
        auto curTim = std::chrono::steady_clock::now();
        std::chrono::duration<float> escapedTim= curTim-lastTim;
        if(std::chrono::duration_cast<std::chrono::seconds>(escapedTim).count()>=5)
        {
            LOG(INFO) << "processEvents()";
            google::FlushLogFiles(google::GLOG_INFO);
            lastTim = curTim;
        }
    }
    
    LOG(INFO) << "get lock and continue to run";
    google::FlushLogFiles(google::GLOG_INFO);
}

您误用了一个条件变量。要使用条件变量,请执行以下操作:

  • 一个线程必须通知另一个线程共享状态的某些更改

  • 实际上,一定有某个共享状态发生了更改

  • 共享状态必须由与条件变量关联的互斥锁保护

  • 在决定等待之前,必须测试共享状态

  • 在发送信号或广播之前,执行信号或广播的线程必须在互斥锁的保护下更改共享状态

  • 如果不遵守这四条规则,代码将始终失败。您似乎没有任何共享状态受到互斥体的保护,您正在使用条件变量通知另一个线程它的更改。如果没有这一点,你就无法做出是否等待的正确决定,最终你将等待已经发生的事情

    有关更多信息,请参阅

    想象一下如果你和你妹妹共用一辆车。你让你妹妹每次开车回来都按门铃,这样你就不用等了。现在想象一下,你想用这辆车,所以你等着铃响。如果你决定等的时候你姐姐没有用车,你会等很长时间的

    您的代码存在此缺陷,因为您的代码决定等待而不首先检查等待的事情是否已经发生,这违反了规则4。您似乎也违反了规则3,因为我没有看到任何受互斥保护的共享状态。您可能违反了规则5,因为我没有看到您的
    工作线程在调用notify函数之前更改任何共享状态

    我在中的示例代码中添加了一些注释,以显示所有规则是如何工作的:

        // condition_variable example
        #include <iostream>           // std::cout
        #include <thread>             // std::thread
        #include <mutex>              // std::mutex, std::unique_lock
        #include <condition_variable> // std::condition_variable
    
        std::mutex mtx;
        std::condition_variable cv;
        bool ready = false;
    
        void print_id (int id) {
          std::unique_lock<std::mutex> lck(mtx);
          while (!ready) cv.wait(lck); // rules 3 and 4 ("ready" is the shared state)
          // ...
          std::cout << "thread " << id << '\n';
        }
    
        void go() {
          std::unique_lock<std::mutex> lck(mtx); // rule 3
          ready = true; // rules 1 and 2
          cv.notify_all(); // rule 5
        }
    
        int main ()
        {
          std::thread threads[10];
          // spawn 10 threads:
          for (int i=0; i<10; ++i)
            threads[i] = std::thread(print_id,i);
    
          std::cout << "10 threads ready to race...\n";
          go();                       // go!
    
          for (auto& th : threads) th.join();
    
          return 0;
        }
    
    //条件变量示例
    #include//std::cout
    #include//std::thread
    #包括//std::mutex、std::unique\u lock
    #include//std::condition\u变量
    std::互斥mtx;
    std::条件变量cv;
    bool ready=false;
    无效打印id(内部id){
    标准:唯一锁定lck(mtx);
    while(!ready)cv.wait(lck);//规则3和4(“ready”是共享状态)
    // ...
    
    大卫的回答很好。我只想澄清几点。看这张图片:

    一个线程是粉红色的,另一个是蓝色的,同步机制是绿色的

    condition变量的主要思想是启用被动同步。所谓被动,我的意思是在(
    while(!producer.has.data())continue;
    循环(
    while(!producer.has.data())continue)时不会在绝望的
    中耗尽CPU。因此,您需要一些共享数据,这些数据将随着程序的发展而更改。您需要一个互斥锁来保护数据。然后,条件变量是睡眠药片和闹钟的组合

    请注意,共享数据只能在锁定的互斥锁下触摸

    请记住,醒来就像按了一次铃。如果你想唤醒的线程没有睡着,它将错过警报。通常这就是你想要的:如果消费者没有睡着,它不需要你的数据(现在)。如果它需要数据,它将在不睡觉的情况下使用它。因此,你可以想象制作人是查尔斯·卓别林,站在传送带旁,每次他“制作”某样东西时,他都会按铃。但他不知道,也不在乎是否有人能听到。也许这就是为什么该功能被称为“通知”的原因而不是信号,因为通常必须接收信号。通知不需要

    图中有一个神秘的“OS”(操作系统)元素。是的,线程“受控”通过条件变量可以被操作系统直接唤醒。这是一些操作系统的工作方式。也许他们想确保没有线程死机。因此,当你醒来时,你必须,必须,必须检查你是被生产者唤醒还是被操作系统唤醒。为此,你需要检查与共享数据状态相关的条件。因此,你需要获取重新锁定(这是自动和原子方式完成的,在图中不清楚)并读取一些共享数据。它可以是一个普通的
    bool shared\u ready
    ,表示“数据已准备好/未准备好”,也可以是数据上的一个条件,如“
    !shared\u container.empty()

    在图中,生产者在被锁定时通知另一个线程。不需要这样,顺序可以颠倒(先解锁,然后通知其他线程)

    如果您已经走到了这一步,那么您已经准备好在CPP参考中进行专业描述了


    请看下面的示例。lambda如何用于检查条件。这是使用条件变量的首选方法:使用它,您不能忘记条件!

    notify\u所有的
    都不会排队。如果在目标线程唤醒并运行时执行它(此处:在while循环内),它将丢失。使用带有谓词的
    wait_for
    作为第三个参数,以检测主线程是否实际发出了“唤醒”信号,并避免虚假唤醒。我被教导永远不要单独使用条件变量,而是使用带有一些信息的共享变量(例如,“正常”bool),例如“数据已经准备好了,您现在可以处理了”。@zkoza谢谢您的澄清。您能给我一个简单的代码片段来解释一下吗
        // condition_variable example
        #include <iostream>           // std::cout
        #include <thread>             // std::thread
        #include <mutex>              // std::mutex, std::unique_lock
        #include <condition_variable> // std::condition_variable
    
        std::mutex mtx;
        std::condition_variable cv;
        bool ready = false;
    
        void print_id (int id) {
          std::unique_lock<std::mutex> lck(mtx);
          while (!ready) cv.wait(lck); // rules 3 and 4 ("ready" is the shared state)
          // ...
          std::cout << "thread " << id << '\n';
        }
    
        void go() {
          std::unique_lock<std::mutex> lck(mtx); // rule 3
          ready = true; // rules 1 and 2
          cv.notify_all(); // rule 5
        }
    
        int main ()
        {
          std::thread threads[10];
          // spawn 10 threads:
          for (int i=0; i<10; ++i)
            threads[i] = std::thread(print_id,i);
    
          std::cout << "10 threads ready to race...\n";
          go();                       // go!
    
          for (auto& th : threads) th.join();
    
          return 0;
        }