本文共 2761 字,大约阅读时间需要 9 分钟。
如有错误请及时指正!
当我们使用
互斥量(Mutex)
与条件变量(condition_variable)
进行多线程同步时有可能会产生虚假唤醒现象, 那么究竟什么是虚假唤醒
,它会在什么情况下被触发,什么情况下无需考虑虚假唤醒,如何避免?
Linux帮助中有提到
在多核处理器下,pthread_cond_signal可能会激活多于一个线程(阻塞在条件变量上的线程)。结果是,当一个线程调用pthread_cond_signal()后,多个调用pthread_cond_wait()或pthread_cond_timedwait()的线程返回。这种效应成为”虚假唤醒”(spurious
wakeup)
通俗点的解释就是:
你(消费线程)收到了其他线程(生产)传来的唤醒信号,但是你唤醒后发现别的消费线程处理的比你快,此时没有数据被可以用于操作,这种情况的发生是预期之外的,称之为虚假唤醒。
以以下C++代码举例。
#include#include #include #include #include #include #include using namespace std;#define PRODUCTER_NUM 10#define CONSUMER_NUM 100std::mutex mutexForProduct;static queue queueForProduct;unsigned uProductTotalNum = 0;condition_variable condVal;//生产线程void ConsumerFunction(unsigned threadIndex){ while(1) { unique_lock locker(mutexForProduct); if(queueForProduct.empty()) { condVal.wait(locker); } if(queueForProduct.empty()) { printf("Consumer[%02u] Want To Buy Product, But There is not remain Product", threadIndex); assert(false); } unsigned productIndex = queueForProduct.front(); printf("Consumer[%02u] Buying Product[%06u], RemainSize= %lu\n", threadIndex, productIndex, queueForProduct.size()); queueForProduct.pop(); }}//消费线程void ProducerFunction(){ while(1) { usleep(100 * 1000); unique_lock locker(mutexForProduct); queueForProduct.push(++uProductTotalNum); condVal.notify_one(); }}int main(){ for(unsigned i = 0; i < CONSUMER_NUM; i++) thread(&ConsumerFunction, i).detach(); for(unsigned i = 0; i < PRODUCTER_NUM; i++) thread(&ProducerFunction).detach(); sleep(3600); return 0;}
当使用notify_all通知消费线程时,会发生虚假唤醒,会有多个消费者线程收到信号被唤醒,当一个线程被唤醒之前,可能其他线程先被唤醒先持有锁,将产品消耗掉。
当生产者的数量(PRODUCTER_NUM)大于1时,无论是使用notify_one,或者是notify_all都会发生虚假唤醒,当多个生产者使用notify_one时,多个线程被唤醒,有可能其中一个处理的特别快,将所有的数据都处理完毕,那么接下来被唤醒的线程都无数据可处理
所以得出以下结论:
只有在生产线程与消费线程都为多个的情况下才有可能发生虚假唤醒,只要有一方的数量为1,就不会发生!
将消费者线程修改如下
void ConsumerFunction(unsigned threadIndex){ while(1) { unique_locklocker(mutexForProduct); while(queueForProduct.empty()) { condVal.wait(locker); } if(queueForProduct.empty()) { printf("Consumer[%02u] Want To Buy Product, But There is not remain Product", threadIndex); assert(false); } unsigned productIndex = queueForProduct.front(); printf("Consumer[%02u] Buying Product[%06u], RemainSize= %lu\n", threadIndex, productIndex, queueForProduct.size()); queueForProduct.pop(); }}
被唤醒后再次判断,若无数据可处理,则应继续休眠。
作者:shizheng163 来源:CSDN 原文: 版权声明:本文为博主原创文章,转载请附上博文链接!