当前位置: 首页 > 知识库问答 >
问题:

使用std::mutex、std::condition_variable和std::unique_lock

桑鸿志
2023-03-14

我在理解条件变量及其在互斥体中的使用时遇到了一些困难,我希望社区能帮助我。请注意,我来自win32背景,因此与CRITICAL_SECTION、HANDLE、SetEvent、WaitForMultipleObject等一起使用。

这是我第一次尝试使用C++11标准库进行并发操作,它是在这里找到的一个程序示例的修改版本。

#include <condition_variable>
#include <mutex>
#include <algorithm>
#include <thread>
#include <queue>
#include <chrono>
#include <iostream>


int _tmain(int argc, _TCHAR* argv[])
{   
    std::queue<unsigned int>    nNumbers;

    std::mutex                  mtxQueue;
    std::condition_variable     cvQueue;
    bool                        m_bQueueLocked = false;

    std::mutex                  mtxQuit;
    std::condition_variable     cvQuit;
    bool                        m_bQuit = false;


    std::thread thrQuit(
        [&]()
        {
            using namespace std;            

            this_thread::sleep_for(chrono::seconds(7));

            // set event by setting the bool variable to true
            // then notifying via the condition variable
            m_bQuit = true;
            cvQuit.notify_all();
        }
    );

    std::thread thrProducer(
        [&]()
        {           
            using namespace std;

            int nNum = 0;
            unique_lock<mutex> lock( mtxQuit );

            while( ( ! m_bQuit ) && 
                   ( cvQuit.wait_for( lock, chrono::milliseconds(10) ) == cv_status::timeout ) )
            {
                nNum ++;

                unique_lock<mutex> qLock(mtxQueue);
                cout << "Produced: " << nNum << "\n";
                nNumbers.push( nNum );              
            }
        }
    );

    std::thread thrConsumer(
        [&]()
        {
            using namespace std;            

            unique_lock<mutex> lock( mtxQuit );

            while( ( ! m_bQuit ) && 
                    ( cvQuit.wait_for( lock, chrono::milliseconds(10) ) == cv_status::timeout ) )
            {
                unique_lock<mutex> qLock(mtxQueue);
                if( nNumbers.size() > 0 )
                {
                    cout << "Consumed: " << nNumbers.front() << "\n";
                    nNumbers.pop();
                }               
            }
        }
    );

    thrQuit.join();
    thrProducer.join();
    thrConsumer.join();

    return 0;
}

关于这个的几个问题。

我读过“任何要等待std::condition_variable的线程必须首先获取std::unique_lock。”

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

我也很有兴趣听到关于这个主题的好的参考资料。

共有1个答案

景哲
2023-03-14

这不会锁定第一个线程中的quit互斥体,从而阻塞第二个线程吗?

是的。

如果这是真的,那么第一个线程如何释放锁,以便另一个线程可以开始?

cvQuit.wait_for( lock, chrono::milliseconds(10) )

我如何调用wait_for()并指定一个零时间,以便wait_for()调用不会阻塞,而是只检查条件并继续?

不要使用条件变量!如果您不想等待一个条件变为true,就不要等待一个条件变量!只需测试m_bquit并继续。(另外,为什么您的布尔函数被称为m_bxxx?它们不是成员,所以m_前缀是误导性的,而b前缀看起来像匈牙利符号的糟糕的MS习惯……这太臭了。)

我也很有兴趣听到关于这个主题的好的参考资料。

请注意。在thrquit中,您写入m_bquit时没有使用互斥体进行保护,因为没有任何东西阻止另一个线程在写入时读取它,所以这是一种竞争条件,即未定义的行为。对bool的写入必须由互斥体保护,或者必须是原子类型,例如std::atomatic

我认为您不需要两个互斥体,它只是增加了争用。由于除非等待condition_variable否则从未释放mtxquit第二个互斥体是没有意义的,mtxquit一个已经确保一次只能有一个线程进入临界段。

 类似资料:
  • 我需要弄清楚lock和condition_variable是如何工作的。 在这里略有修改的cplusplusreference代码中 我感到困惑的是,如果worker_thread已经锁定了互斥体,主线程如何锁定它。 我不明白为什么在这种情况下会出现“运行时错误”。

  • 我知道互斥体通常也会保护共享数据,使用原子变量只是一个例子。问题不是如何保护共享数据,而是是否需要使用相同的互斥体来保护两者。另一个使用第二互斥体的示例:

  • 并行开发挺复杂的,特别是在试图用好线程和锁的过程中。如果要用到条件变量或std-atomics(一种无锁开发方式),那就更复杂了。C++0x提供了future和promise来简化任务线程间的返回值操作;同时为启动任务线程提供了packaged_task以方便操作。其中的关键点是允许2个任务间使用无(显式)锁的方式进行值传递;标准库帮你高效的做好这些了。基本思路很简单:当一个任务需要向父线程(启动

  • 标准库函数bind()和function()定义于头文件<functional>中(该头文件还包括许多其他函数对象),用于处理函数及函数参数。bind()接受一个函数(或者函数对象,或者任何你可以通过”(…)”符号调用的事物),生成一个其有某一个或多个函数参数被“绑定”或重新组织的函数对象。(译注:顾名思义,bind()函数的意义就像它的函数名一样,是用来绑定函数调用的某些参数的。)例如: int

  • 在我的Fedora 34环境(g)中,定义为: 如果表达式已经是右值,那么