https://www.youtube.com/watch?v=13dFggo4t_I视频地址
实例1
考虑这样一个场景:存在一个全局队列deque,线程A向deque中推入数据(写),线程B从deque中取出数据(读).
deque这个资源对象就需要用mutex做访问控制,代码如下:
std::deque<int> q;
std::mutex mu;
void func1() {
int ct = 10;
while (ct > 0) {
std::unique_lock<std::mutex> lock(mu);
q.push_front(ct);
lock.unlock();
std::this_thread::sleep_for(chrono::seconds(1));
ct --;
}
}
void func2() {
int data = 0;
while (data != 1 ) {
std::unique_lock<std::mutex> lock(mu);
if (!q.empty()) {
data = q.back();
q.pop_back();
lock.unlock();
...
} else {
lock.unlock();
}
}
}
int main() {
std::thread t1(func1);
std::thread t2(func2);
t1.join();
t2.join();
return 0;
}
线程t1中,循环向队列中推入数据,每次睡眠1秒;做为“生产者”.
线程t2中,不断地从队列中读出数据(先判断是否有数据);做为“消费者”.
存在一个问题:线程t2中,会先判断deque是否为空;如果为空,会执行unlock,然后马上进入下一次循环,造成busy waiting(反复频繁地判断某一状态是否为true).
一种解决方法如下:
void func2() {
...
} else {
lock.unlock();
std::this_thread::sleep_for(chrono::seconds(1));
}
}
在发现队列为空时睡眠一段时间time,可以一定程度上解决问题。
但是变量time设置成什么数值并不好确定;设置的过小,可能会回到busy waiting;设置的过大,会导致不能及时的获取数据
Condition variable
用条件变量可以很好的处理这个问题,让消费者不用“盲目等待”
std::deque<int> q;
std::mutex mu;
std::condition_variable cond;
void func1() {
int ct = 10;
while (ct > 0) {
std::unique_lock<std::mutex> lock(mu);
q.push_front(ct);
lock.unlock();
cond.notify_one(); // wake up one waiting thread
// cond.notify_all(); // wake up all waiting threads
std::this_thread::sleep_for(chrono::seconds(1));
ct --;
}
}
void func2() {
int data = 0;
while (data != 1 ) {
std::unique_lock<std::mutex> lock(mu);
cond.wait(lock, [](){ return !q.empty(); });
data = q.back();
q.pop_back();
lock.unlock();
}
}
生产者线程中,数据被推入deque后执行notify_one(),就可以唤醒某一个线程;类似于银行柜台叫号.
消费者线程中,只需要cond.wait(lock,...),睡眠等待,直到被“叫号”;睡眠等待过程中不占用cpu时间.(被唤醒时,第二个参数如果返回false会继续睡眠,为true则会往下执行)
注意这里cond.wait(lock)进入睡眠之前会先释放对lock的占用;被唤醒时会先占用mutex.
小结
condition_variable粗略的说,可能类似一个“唤醒服务”.
原文地址:https://www.cnblogs.com/ChenLambda/p/11832656.html
时间: 2024-10-14 08:41:01