C++标准库对条件变量有两套实现:std::condition_variable
和 std::condition_variable_any
。 这两个实现都包含在 <condition_variable>
头文件的声明中。 两者都需要与一个互斥量一起才能工作(互斥量是为了同步); 前者仅限于与 std::mutex 一起工作, 而后者可以和任何满足最低标准的互斥量一起工作, 从而加上了_any的后缀。
std::condition_variable 构造函数
condition_variable(); //default
condition_variable (const condition_variable&) = delete; // copy delete
看一个例子:
std::mutex mut;
std::queue<data_chunk> data_queue; // 1
std::condition_variable data_cond;
void data_preparation_thread()
{
while(more_data_to_prepare())
{
data_chunk const data=prepare_data();
std::lock_guard<std::mutex> lk(mut);
data_queue.push(data); // 2
data_cond.notify_one(); // 3
}
}
void data_processing_thread()
{
while(true)
{
std::unique_lock<std::mutex> lk(mut); // 4
data_cond.wait(lk,[]{return !data_queue.empty();}); // 5
data_chunk data=data_queue.front();
data_queue.pop();
lk.unlock(); // 6
process(data);
if(is_last_chunk(data))
break;
}
}
首先, 你拥有一个用来在两个线程之间传递数据的队列①。 当数据准备好时, 使用
std::lock_guard
对队列上锁, 将准备好的数据压入队列中②, 之后线程会对队列中的数据上锁。 然后调用 std::condition_variable
的notify_one()
成员函数, 对等待的线程(如果有等待线程)进行通知③。
在另外一侧, 你有一个正在处理数据的线程, 这个线程首先对互斥量上锁, 但在这里 std::unique_lock
要比
std::lock_guard
④更加合适。 线程之后会调用 std::condition_variable
的成员函数wait()
, 传递一个锁和一个lambda函数表达式(作为等待的条件⑤)。 在这个例子中, 简单的lambda函数[]{return !data_queue.empty();}
会去检查data_queue是否不为空, 当data_queue不为空——那就意味着队列中已经准备好数据了。
std::condition_variable::wait()
void wait (unique_lock<mutex>& lck); // unconditional
template <class Predicate>
void wait (unique_lock<mutex>& lck, Predicate pred); // predicate
std::condition_variable
提供了两种 wait() 函数。当前线程调用 wait() 后将被阻塞(此时当前线程应该获得了锁(mutex),不妨设获得锁 lck),直到另外某个线程调用 notify_* 唤醒了当前线程。
在线程被阻塞时,该函数会自动调用 lck.unlock() 释放锁,使得其他被阻塞在锁竞争上的线程得以继续执行。
另外,一旦当前线程获得通知(notified,通常是另外某个线程调用 notify_* 唤醒了当前线程),wait() 函数也是自动调用 lck.lock(),使得 lck 的状态和 wait 函数被调用时相同。 这就是为什么用
std::unique_lock
而不使用 std::lock_guard —等待中的线程必须在等待期间解锁互斥量, 并在这这之后对互斥量再次上锁, 而 std::lock_guard 没有这么灵活。
在第二种情况下(即设置了 Predicate),只有当 pred 条件为 false 时调用 wait() 才会阻塞当前线程,并且在收到其他线程的通知后只有当 pred 为 true 时才会被解除阻塞。
std::condition_variable::wait_for()
template <class Rep, class Period>
cv_status wait_for (unique_lock<mutex>& lck,
const chrono::duration<Rep,Period>& rel_time); // unconditional
template <class Rep, class Period, class Predicate>
bool wait_for (unique_lock<mutex>& lck,
const chrono::duration<Rep,Period>& rel_time, Predicate pred); // predicate
与 std::condition_variable::wait()
类似,不过 wait_for
可以指定一个时间段,在当前线程收到通知或者指定的时间 rel_time 超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其他线程的通知,wait_for
返回,剩下的处理步骤和 wait() 类似。wait_for
的重载版本(predicte)的最后一个参数 pred 表示 wait_for
的预测条件,只有当 pred 条件为 false 时调用 wait() 才会阻塞当前线程,并且在收到其他线程的通知后只有当 pred 为 true 时才会被解除阻塞。
std::condition_variable::wait_until
template <class Clock, class Duration>
cv_status wait_until (unique_lock<mutex>& lck,
const chrono::time_point<Clock,Duration>& abs_time); // unconditional
template <class Clock, class Duration, class Predicate>
bool wait_until (unique_lock<mutex>& lck,
const chrono::time_point<Clock,Duration>& abs_time,
Predicate pred); // predicate
与 std::condition_variable::wait_for
类似,但是 wait_until
可以指定一个时间点,在当前线程收到通知或者指定的时间点 abs_time 超时之前,该线程都会处于阻塞状态。而一旦超时或者收到了其他线程的通知,wait_until 返回,剩下的处理步骤和 wait_until() 类似。
另外,wait_until 的重载版本(predicte)的最后一个参数 pred 表示 wait_until 的预测条件,只有当 pred 条件为 false 时调用 wait() 才会阻塞当前线程,并且在收到其他线程的通知后只有当 pred 为 true 时才会被解除阻塞。
std::condition_variable::notify_one()
唤醒某个等待(wait)线程。如果当前没有等待线程,则该函数什么也不做,如果同时存在多个等待线程,则唤醒某个线程是不确定的(unspecified)。
std::condition_variable::notify_all()
唤醒所有的等待(wait)线程。如果当前没有等待线程,则该函数什么也不做。
标签:std,变量,线程,variable,data,condition,wait From: https://www.cnblogs.com/love-9/p/18094422