【condition_variable】C++条件变量的原理和用法
condition_variable简介
condition_variable类是一个同步原语,与std::mutex一起使用,用于阻塞一个或多个线程,直到另一个线程修改一个共享变量(条件)并通知condition_variable。condition_variable主要有wait函数和notify_* 函数,wait的作用是等待,notify的作用是通知。Linux环境下的相关接口pthread_cond_*等函数。
当 std::condition_variable对象的某个wait 函数被调用的时候,它使用 std::unique_lock(通过 std::mutex) 来锁住当前线程。当前线程会一直被阻塞,直到另外一个线程在相同的 std::condition_variable 对象上调用了 notification 函数来唤醒当前线程。
wait()还有第二个参数,这个参数接收一个布尔类型的值,当这个布尔类型的值为false的时候线程就会被阻塞在这里,只有当线程被唤醒之后,且第二参数为true才会往下运行。
notify_one()每次只能唤醒一个线程,notify_all()函数的作用就是可以唤醒所有的线程,但是最终能抢夺锁的只有一个线程,或者说有多个线程在wait,但是用notify_one()去唤醒其中一个线程,那么这些线程就出现了去争夺互斥量的情况,最终没有获得锁的控制权的线程就会再次回到阻塞的状态,对于这些没有抢到控制权的过程叫做虚假唤醒。对于虚假唤醒的解决方法就是加一个while()循环,比如:
while(que.size() == 0)
{
cr.wait(lck);
}
或者
if(que.empty())
{
cr.wait(clk);
}
这个就是当线程被唤醒以后,先进行判断,是否可以去操作,如果可以,再去运行下面的代码,否则继续在循环内执行wait函数
上面所说多个线程等待一个唤醒的情况叫做惊群效应。
condition_variable类
std::condition_variable::wait():
方法原型:
void wait (unique_lock<mutex>& lck, Predicate pred);
std::condition_variable提供了两种wait()函数。当前线程调用wait()后被阻塞(此时当前线程获得了mutex),直到另外某个线程调用notify_*唤醒了当前线程。
在当前线程被阻塞时,该函数会自动调用lck.unlock()释放锁,使得其他被阻塞在锁竞争上的线程得以继续执行。一旦当前线程获得通知(通常是另外某个线程调用notify_* 唤醒了当前线程),wait()函数也是自动调用lck.lock(),使得lck的状态和wait函数被调用时相同。
在设置了pred的情况下,只有当pred条件为false时调用wait()才会阻塞当前线程,并且在收到其他线程的通知后只有当pred为true时才会被解除阻塞。等效于以下情景。
while(!pred())
{
wait(lck);
}
std::condition_variable::wait_for():
方法原型:
cv_status wait_for (unique_lock<mutex>& lck,
const chrono::duration<rep,period>& rel_time);
bool wait_for (unique_lock<mutex>& lck,
const chrono::duration<rep,period>& rel_time, Predicate pred);
与std::condition_variable::wait()类似,不过wait_for可以指定一个时间段,在当前线程收到通知或者指定的时间rel_time超时之前,该线程都会处于阻塞状态。一旦超时或收到了其他线程的通知,wait_for返回,剩下的处理步骤和wait()类似。
wait_for()的pred表示预测条件,只有当pred条件为false时调用wait()才会阻塞当前线程,并且在收到其他线程的通知后只有当pred为true时才会被解除阻塞。
std::condition_variable::wait_until():
方法原型:
cv_status wait_until (unique_lock<mutex>& lck,
const chrono::time_point<clock,duration>& abs_time);
bool wait_until (unique_lock<mutex>& lck,
const chrono::time_point<clock,duration>& abs_time,
Predicate pred);
与wait_for类似,但是wait_until可以指定一个时间点,在当前线程收到通知或者指定的时间点abs_time超时之前,该线程都处于阻塞状态。一旦超时或者收到了其他线程的通知,wait_until返回,剩下的处理步骤和wait()相似
wait_until的重载版本中pred表示wait_until的预测条件,只有当pred条件为false时调用wait()才会阻塞当前线程,并且在收到其他线程的通知后只有当pred为true时才会被解除阻塞
std::condition_variable::notify_one():
唤醒某个等待(wait)线程。如果当前没有等待线程,则该函数什么也不做,如果同时存在多个等待线程,则唤醒某个线程是不确定的。
std::condition_variable::notify_all():
唤醒所有等待(wait)线程。如果当前没有等待线程,则什么也不做。
辅助函数std::notify_all_at_thread_exit():
通知其他线程,给定的线程已经全部完成,当调用该函数的线程退出时,所有在cond条件变量上等待的线程都会收到通知
std::condition_variable_any类
与std::condition_variable类似,只不过std::condition_variable_any的wait函数可以接受任何lockadle参数。而std::condition_variable只能接受std::unique_lock类型的参数,除此意外和std::condition_variable几乎完全一样。
condition_variable代码实例
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
void worker_thread()
{
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m);
// wait第二个参数返回值为bool类型,如果返回为false当前线程则被锁住
cv.wait(lk, []{return ready;});
// after the wait, we own the lock.
std::cout << "Worker thread is processing data\n";
data += " after processing";
// Send data back to main()
processed = true;
std::cout << "Worker thread signals data processing completed\n";
// Manual unlocking is done before notifying, to avoid waking up
// the waiting thread only to block again (see notify_one for details)
lk.unlock();
cv.notify_one();
}
int main()
{
std::thread worker(worker_thread);
data = "Example data";
// send data to the worker thread
{
std::lock_guard<std::mutex> lk(m);
ready = true;
std::cout << "main() signals data ready for processing\n";
}
cv.notify_one();
// wait for the worker
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return processed;});
}
std::cout << "Back in main(), data = " << data << '\n';
worker.join();
}
运行结果:
main() signals data ready for processing
Worker thread is processing data
Worker thread signals data processing completed
Back in main(), data = Example data after processing
参考文章:
https://cloud.tencent.com/developer/article/1584067
https://en.cppreference.com/w/cpp/thread/condition_variable
https://blog.csdn.net/lv0918_qian/article/details/81745723
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)