我在理解条件变量及其在互斥体中的使用时遇到了一些困难,我希望社区能帮助我。请注意,我来自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);
我也很有兴趣听到关于这个主题的好的参考资料。
这不会锁定第一个线程中的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)中,定义为: 如果表达式已经是右值,那么