为什么conditional_variable::notify_all可能不会唤醒任何线程?

我用conditional_variable::notify_all()唤醒一个等待的线程(只有一个线程在等待unique_lock 确实)。

这个代码片段在大多数情况下都运行良好,但日志文件(详细信息见下文)表明unique_lock在新创建的线程已经返回后,父线程无法获取。

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

这是相关的代码片段:

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);
}

以下是程序无法正常工作时的相关日志:

Log line format: [IWEF]hh:mm:ss.uuuuuu threadid file:line] msg
20:19:14.638686 272568 mainwindow.cpp:208] to leave the subthread
20:19:17.669246 10256 mainwindow.cpp:221] processEvents()
20:19:22.678846 10256 mainwindow.cpp:221] processEvents()
20:19:17.669246 10256 mainwindow.cpp:221] processEvents()
20:19:22.678846 10256 mainwindow.cpp:221] processEvents()
20:19:17.669246 10256 mainwindow.cpp:221] processEvents()
20:19:22.678846 10256 mainwindow.cpp:221] processEvents()
20:19:17.669246 10256 mainwindow.cpp:221] processEvents()
...

回答

您正在滥用条件变量。要使用条件变量:

  1. 一个线程必须通知另一个线程有关共享状态的某些更改。

  2. 实际上必须有一些共享状态发生了变化。

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

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

  5. 执行信号或广播的线程必须在发出信号或广播之前在互斥锁的保护下改变了共享状态。

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

有关更多信息,请参阅此答案。

想象一下,如果你和你妹妹共用一辆车。你让你姐姐在她把车开回来时按铃,这样你就可以不用等车了。现在想象一下你想使用汽车,所以你等待铃响。当你决定等待时,如果你的妹妹没有使用汽车,你将等待很长时间!

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

我在此处的示例代码中添加了一些注释,以展示所有规则的工作原理:

    // 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;
    }


以上是为什么conditional_variable::notify_all可能不会唤醒任何线程?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>