条件变量
C++11 提供了条件变量condition variable用于实现线程间的同步操作,需要包含头文件#include <condition_variable> ,并且一般都是与互斥量mutex配合使用。
条件变量的工作方式为:
- 使用条件变量的线程首先对互斥量加锁
- 检查某个条件,如果条件不满足,则释放互斥锁量,进入休眠;如果条件满足,则继续执行资源操作,执行完操作后释放互斥量
- 另外一个线程加锁执行资源操作,使条件满足后,释放互斥量,唤醒等待的线程
具体参考:std::condition_variable
成员函数
wait ——等待某个条件满足时,重新获取互斥量加锁并继续执行;否则释放互斥量并休眠,直至重新被唤醒再重新判断一次。互斥量必须使用unique_lock 对象。 包含两个原型
void wait(unique_lock<mutex>& __lock)
template<typename _Predicate>
void wait(unique_lock<mutex>& __lock, _Predicate __p)
_Predicate 对象是一个判断条件是否满足的函数。1)如果调用的原型没有该参数,那么第一次调用 wait 时默认条件不成立,然后进入休眠,直至其他线程唤醒它,随后进行常规的互斥量加锁操作:获取不到锁则休眠直至重新获取到锁。2)如果调用的原型有该参数,wait 会调用该函数判断返回结果是否为 true :是,则继续执行;否,则释放互斥量并阻塞休眠,直到其他线程唤醒它,此时如果条件还不满足,则线程继续休眠等待,直到有一次唤醒后条件满足了,那么它就从新对互斥量加锁并继续执行后面的操作。
wait_for ——在 wait 的基础上增加了一个超时时间参数,用法类似于 mutex 的 try_lock_for()wait_until ——类似于mutex 的 try_lock_until()notify_one ——前面提到调用wait的线程需要其他线程唤醒,那么自然就会想到如何唤醒。notify_one 唤醒多个调用 wait 后正在休眠的线程中的一个。notify_all ——唤醒正在休眠等待的所有线程,但它们中只有一个能够获得互斥量!
代码示例
下面的示例使用条件变量实现一个同步队列。
定义类
template<typename T>
class SyncQueue
{
public:
SyncQueue() {}
~SyncQueue() {}
void Put(const T & one)
{
unique_lock<mutex> locker(_mutex);
_queue.emplace_back(one);
_notEmpty.notify_one();
}
T & Take()
{
unique_lock<mutex> locker(_mutex);
_notEmpty.wait(locker, [this](){ return !_queue.empty(); });
T& one = _queue.front();
_queue.pop_front();
return one;
}
bool isEmpty()
{
unique_lock<mutex> locker(_mutex);
return _queue.empty();
}
size_t Size()
{
unique_lock<mutex> locker(_mutex);
return _queue.size();
}
private:
list<T> _queue;
mutex _mutex;
condition_variable _notEmpty;
};
测试例程
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <list>
using namespace std;
SyncQueue<int> sq;
void PutData()
{
for(int i = 0; i < 20; i++)
{
sq.Put(i);
cout << "# sq put " << i << endl;
}
}
void TakeData()
{
for(int i = 0; i < 20; i++)
{
int one = sq.Take();
cout << "# sq take " << one << endl;
}
}
int main()
{
thread t1(PutData);
thread t2(TakeData);
t1.join();
t2.join();
}
|