如何正确使用 std::condition_variable?

Posted

技术标签:

【中文标题】如何正确使用 std::condition_variable?【英文标题】:How to use a std::condition_variable correctly? 【发布时间】:2017-05-10 16:30:54 【问题描述】:

我对@9​​87654321@ 以及如何(安全地)使用它们感到困惑。在我的应用程序中,我有一个创建 gui 线程的类,但是当 gui 由 gui 线程构造时,主线程需要等待。

情况与下面的函数相同。主线程创建一个互斥体、锁和condition_variable。然后它制作线程。虽然这个 worker thread 还没有通过某个点(这里打印数字),但不允许主线程继续(即必须 wait 打印所有数字)。

在这种情况下如何正确使用condition_variables?另外,我读到自发唤醒是一个问题。我该如何处理它们?

    int main()
    
        std::mutex mtx;
        std::unique_lock<std::mutex> lck(mtx);
        std::condition_variable convar;

        auto worker = std::thread([&]
            /* Do some work. Main-thread can not continue. */
            for(int i=0; i<100; ++i) std::cout<<i<<" ";
            convar.notify_all(); // let main thread continue
            std::cout<<"\nworker done"<<std::endl;       
        );


        // The main thread can do some work but then must wait until the worker has done it's calculations.
        /* do some stuff */
        convar.wait(lck);
        std::cout<<"\nmain can continue"<<std::endl; // allowed before worker is entirely finished
        worker.join();
    

【问题讨论】:

为什么“正确”?您是否期望人们默认会解释如何错误地使用某些东西?! “安全”是什么意思? 【参考方案1】:

通常情况下,您会阻止一些可观察到的共享状态:

bool done = false;
std::mutex done_mx;
std::condition_variable done_cv;


  std::unique_lock<std::mutex> lock(done_mx);

  std::thread worker([&]() 
    // ...
    std::lock_guard<std::mutex> lock(done_mx);
    done = true;
    done_cv.notify_one();
  );

  while (true)  done_cv.wait(lock); if (done) break; 

  // ready, do other work

  worker.join();

请注意,您在循环中等待,直到满足实际条件。另请注意,对实际共享状态 (done) 的访问是通过互斥锁 done_mx 序列化的,每当访问 done 时,该互斥锁都会被锁定。

有一个辅助成员函数可以为您执行条件检查,因此您不需要循环:

done_cv.wait(lock, [&]()  return done; );

【讨论】:

为什么需要 lock_guard? @dani,在离开作用域时自动解锁互斥锁。 我只想说,永远不要使用带有自动引用捕获的 lambda 作为线程函数。所以。许多。错误。 @ZanLynx:什么错误? @KerrekSB:我认为你这里没有,但我见过这么多。例如,添加一个不受互斥锁保护的变量的使用。在退出的函数中使用对堆栈变量的引用。

以上是关于如何正确使用 std::condition_variable?的主要内容,如果未能解决你的问题,请参考以下文章

如何正确使用 Composer 安装 Laravel 扩展包

如何正确的使用SharedPreferences

如何正确强制正确使用类方法?

如何正确使用 Composer 安装 Laravel 扩展包

如何正确使用 AsyncTask? [关闭]

如何正确的使用QWebEngineView