c++ 11 condition_variable 等待虚假唤醒不起作用
c++ 11 condition_variable wait spurious wake up is not working
我尝试使用 condition_variable
、
编写一个简单的 producer/consumer
include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <chrono>
#include <queue>
#include <chrono>
using namespace std;
condition_variable cond_var;
mutex m;
int main()
{
int c = 0;
bool done = false;
cout << boolalpha;
queue<int> goods;
thread producer([&](){
for (int i = 0; i < 10; ++i) {
m.lock();
goods.push(i);
c++;
cout << "produce " << i << endl;
m.unlock();
cond_var.notify_one();
this_thread::sleep_for(chrono::milliseconds(100));
}
done = true;
cout << "producer done." << endl;
cond_var.notify_one();
});
thread consumer([&](){
unique_lock<mutex> lock(m);
while(!done || !goods.empty()){
/*
cond_var.wait(lock, [&goods, &done](){
cout << "spurious wake check" << done <<endl;
return (!goods.empty() || done);
});
*/
while(goods.empty())
{
cout<< "consumer wait" <<endl;
cout<< "consumer owns lock " << lock.owns_lock() <<endl;
cond_var.wait(lock);
}
if (!goods.empty()){
cout << "consume " << goods.front()<<endl;
goods.pop();
c--;
}
}
});
producer.join();
consumer.join();
cout << "Net: " << c << endl;
}
我现在遇到的问题是当消费者消费生产者设置done
到[=15=之前的最后一个项目时,消费者线程会卡在
while(goods.empty())
{
cout<< "consumer wait" <<endl;
cout<< "consumer owns lock " << lock.owns_lock() <<endl;
cond_var.wait(lock);
}
我的理解是cond_var.wait(lock)
会虚假唤醒从而退出while(good.empty())
循环,但好像不是这样?
虚假唤醒并非经常发生,您可以依赖它以您建议的方式打破循环。发生虚假唤醒的风险是您必须考虑的当前条件变量实现的不幸 side-effect,但无法保证您何时(如果有的话)会遇到虚假唤醒。
如果您想确保消费者线程不会因等待永远不会到来的通知而卡住,您可以尝试使用 std::condition_variable::wait_for()
。它需要一个持续时间,如果持续时间到期,它将超时并重新获取锁。它可能被视为更接近繁忙的等待,但如果超时足够长,对性能的影响应该可以忽略不计。
正如@Karlinde 所说,顾名思义,虚假唤醒不一定会发生。相反,它们通常根本不会发生。
但是,即使会发生虚假唤醒,这也不能解决您的问题:您的程序中只是有一个无限循环。一旦生产者停止,goods.empty()
为真,它永远不会再改变。因此将 while 循环更改为:
while(!done && goods.empty())
{
...
}
现在它应该退出...大多数时候。你仍然有一个可能的竞争条件,因为在生产者中,你设置了 done = true
而没有持有锁。
如果生产者在没有任何消费者等待{cond_var.wait(锁定);}的情况下通知{cond_var.notify_one();},那么发送给消费者的第一个通知就不会被注意到。
@tesla1060
“我现在遇到的问题是,当消费者在生产者将 done 设置为 true 之前消费最后一项时,消费者线程将陷入困境”,这是不正确的。事实上,消费者没有收到生产者的任何通知(因为它错过了一个通知(第一个))。
我尝试使用 condition_variable
、
include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <chrono>
#include <queue>
#include <chrono>
using namespace std;
condition_variable cond_var;
mutex m;
int main()
{
int c = 0;
bool done = false;
cout << boolalpha;
queue<int> goods;
thread producer([&](){
for (int i = 0; i < 10; ++i) {
m.lock();
goods.push(i);
c++;
cout << "produce " << i << endl;
m.unlock();
cond_var.notify_one();
this_thread::sleep_for(chrono::milliseconds(100));
}
done = true;
cout << "producer done." << endl;
cond_var.notify_one();
});
thread consumer([&](){
unique_lock<mutex> lock(m);
while(!done || !goods.empty()){
/*
cond_var.wait(lock, [&goods, &done](){
cout << "spurious wake check" << done <<endl;
return (!goods.empty() || done);
});
*/
while(goods.empty())
{
cout<< "consumer wait" <<endl;
cout<< "consumer owns lock " << lock.owns_lock() <<endl;
cond_var.wait(lock);
}
if (!goods.empty()){
cout << "consume " << goods.front()<<endl;
goods.pop();
c--;
}
}
});
producer.join();
consumer.join();
cout << "Net: " << c << endl;
}
我现在遇到的问题是当消费者消费生产者设置done
到[=15=之前的最后一个项目时,消费者线程会卡在
while(goods.empty())
{
cout<< "consumer wait" <<endl;
cout<< "consumer owns lock " << lock.owns_lock() <<endl;
cond_var.wait(lock);
}
我的理解是cond_var.wait(lock)
会虚假唤醒从而退出while(good.empty())
循环,但好像不是这样?
虚假唤醒并非经常发生,您可以依赖它以您建议的方式打破循环。发生虚假唤醒的风险是您必须考虑的当前条件变量实现的不幸 side-effect,但无法保证您何时(如果有的话)会遇到虚假唤醒。
如果您想确保消费者线程不会因等待永远不会到来的通知而卡住,您可以尝试使用 std::condition_variable::wait_for()
。它需要一个持续时间,如果持续时间到期,它将超时并重新获取锁。它可能被视为更接近繁忙的等待,但如果超时足够长,对性能的影响应该可以忽略不计。
正如@Karlinde 所说,顾名思义,虚假唤醒不一定会发生。相反,它们通常根本不会发生。
但是,即使会发生虚假唤醒,这也不能解决您的问题:您的程序中只是有一个无限循环。一旦生产者停止,goods.empty()
为真,它永远不会再改变。因此将 while 循环更改为:
while(!done && goods.empty())
{
...
}
现在它应该退出...大多数时候。你仍然有一个可能的竞争条件,因为在生产者中,你设置了 done = true
而没有持有锁。
如果生产者在没有任何消费者等待{cond_var.wait(锁定);}的情况下通知{cond_var.notify_one();},那么发送给消费者的第一个通知就不会被注意到。
@tesla1060 “我现在遇到的问题是,当消费者在生产者将 done 设置为 true 之前消费最后一项时,消费者线程将陷入困境”,这是不正确的。事实上,消费者没有收到生产者的任何通知(因为它错过了一个通知(第一个))。