条件变量(condition_variable)是一个和条件相关的类,通常和互斥量配合使用。
本文主要介绍条件变量的两个函数wait() 和notify_one()。
wait() 阻塞程序,等待条件的达成。notify_one() 把被阻塞在wait()的线程唤醒。
在库函数中,函数wait有两个重载。
void wait(unique_lock& _Lck)
{ // wait for signal
_Cnd_waitX(_Mycnd(), _Lck.mutex()->_Mymtx());
}
template
void wait(unique_lock& _Lck, _Predicate _Pred)
{ // wait for signal and test predicate
while (!_Pred())
wait(_Lck);
}
函数有两个参数,程序执行到wait()语句时:
(1).对互斥量加锁。
(2).执行第二个参数表示的函数 _Pred。
(3).函数 _Pred 返回false时,对互斥量解锁,程序阻塞,等待其它线程调用nofify_one()将其唤醒后,回到步骤(1)。
(4).函数 _Pred返回true时,程序继续执行。
函数只有一个参数,程序执行到wait()语句时阻塞(相当于第二参数表示的函数始终返回false),等待其它线程调用nofify_one()将其唤醒后,再继续执行(相当于第二参数表示的函数始终返回true)。
#include
#include
#include
#include
using namespace std;
class MsgManage
{
public:
MsgManage() {}
~MsgManage() {}
void InMsg()
{
for (int i = 0; i < 10000; i++)
{
cout << "插入元素: " << i << endl;
std::unique_lock guard(myMutex);
myList.push_back(i);
//把被阻塞在wait()的线程唤醒
condition.notify_one();
}
}
void OutMsg()
{
int num;
while (true)
{
std::unique_lock guard(myMutex);
//列表为空时,对互斥量解锁,程序阻塞,等待被唤醒。
//列表不为空时,程序继续执行。
condition.wait(guard, [this] {
if (!myList.empty())
return true;
return false;
});
//程序执行到这里,列表不为空,且互斥量已被加锁
num = myList.front();
myList.pop_front();
//解锁,避免互斥量被锁住太长时间
guard.unlock();
cout << "移除元素: " << num << endl;
//程序继续执行其它耗时代码
}
}
private:
list myList;
mutex myMutex;
condition_variable condition; //条件变量对象和互斥量配合使用
};
int main()
{
MsgManage manage;
thread outMsg(&MsgManage::OutMsg, &manage);
thread inMsg(&MsgManage::InMsg, &manage);
inMsg.join();
outMsg.join();
return 0;
}
上述代码很好的演示了程序的阻塞和唤醒,减少了很多无意义的条件判断。但是依然存在一些问题。
(1) 执行notify_one() 时,如果没有线程阻塞在wati()时,此次唤醒就没有实际效果。
(2)当函数InMsg()执行完10000次循环后,不再会执行notify_one(),此时很可能有线程阻塞在wait()上,永远不会被唤醒了。
对于此问题,后续的文章中会做进一步改进。