条件变量
条件变量(condition_variable)用来实现多线程间的同步操作,控制多个线程的执行顺序。
操作 | 作用 |
---|---|
conditon_variable cv | 默认构造函数 |
~conditon_variable() | 析构函数 |
cv.notify_one() | 唤醒一个等待线程,若没有等待线程,通知则被丢弃。 |
cv.notify_all() | 唤醒所有等待线程 |
cv.wait(ul) | 使用unique_lock来等待通知 |
cv.wait(ul, pred) | 使用unique_lock来等待通知,直到唤醒时pred结果为true |
cv.wait_for(ul, duration) | 在duration时间内使用unique_lock来等待通知 |
cv.wait_for(ul, duration, pred) | 在duration时间内使用unique_lock来等待通知,或直到唤醒时pred结果为true |
cv.wait_until(ul, timepoint) | 在timepoint时间点前使用unique_lock来等待通知 |
cv.wait_until(ul, timepoint, pred) | 在timepoint时间点前使用unique_lock来等待通知,或直到唤醒时pred结果为true |
例子
queue<int> queue;
mutex queMutex;
condition_variable queCV;
void provider(int val)
{
for (int i = 0; i < 6; i ++)
{
{
lock_guard<mutex> lg(queMutex);
queue.push(val + 1);
}
queCV.notify_one();
this_thread.sleep_for(chrono::milliseconds(val));
}
}
void consumer(int num)
{
while (true)
{
int val;
{
unique_lock<mutex> ul(queMutex);
queCV.wait(ul, []{ return !queue.empty(); });
val = queue.front();
queue.pop();
}
}
}
int main()
{
auto p1 = async(launch::async, provider, 100);
auto p2 = async(launch::async, provider, 300);
auto p3 = async(launch::async, provider, 500);
auto c1 = async(launch::async, consumer, 1);
auto c2 = async(launch::async, consumer, 2);
}
条件变量只是负责多线程的同步控制,资源的竞争访问还需要互斥锁,条件变量内部实现也使用了unique_lock。所以条件变量总是和互斥锁一起使用。
需要注意的是,代码中通知函数notify前使用的是lock_guard,而等待函数wait前使用的是unique_lock。
{
lock_guard<mutex> lg(queMutex);
queue.push(val + 1);
}
queCV.notify_one();
unique_lock<mutex> ul(queMutex);
queCV.wait(ul, []{ return !queue.empty(); });
unique_lock相比lock_guard更加灵活。lock_guard只能在构造函数中加锁,在析构函数中解锁;而unique_lock还可以在需要时调用lock或unlock进行加锁、解锁操作。等待函数wait阻塞时,会调用unique_lock的unlock函数释放锁,以使其他线程有机会获取锁。
另一个需要注意的是条件变量的虚假唤醒。虚假唤醒是指,由于操作系统的原因,条件变量的wait操作可能在条件变量未被notify通知时返回。所以需要进行二次检查。
queCV.wait(ul, []{ return !queue.empty(); });
wait函数内部会在条件变量被唤醒时调用第二实参,当它返回true时才会返回,否则再次进行等待。相当于:
while (queue.empty())
{
queCV.wait(ul);
}
另外,如果调用wait时第二实参的返回值是true,那么wait不会等待而是马上返回。下面代码不调用notify函数也能正常运行。
auto lmb = [] {
std::unique_lock<std::mutex> ul(m);
cv.wait(ul, [] { return true; });
std::cout << "test condition variable wait" << std::endl;
};
std::future<void> f = std::async(std::launch::async, lmb);
f.get();