虚假唤醒


虚假唤醒

https://zhuanlan.zhihu.com/p/652823880
https://www.cnblogs.com/angdh/p/18267978
https://cloud.tencent.com/developer/article/1557403

应用层引起的虚假唤起

这种虚假唤起是由不合理的代码逻辑引起的,具体看代码

#include <condition_variable>
#include <iostream>
#include <thread>
#include <queue>
#include <string>

using namespace std::chrono_literals;
std::condition_variable cv;
std::mutex mtx;
std::queue<std::string> q;

void Producer()
{
    std::this_thread::sleep_for(2000ms);
    std::cout << "Ready Send notification." << std::endl;
    mtx.lock();
    q.push("message");
    q.push("message");
    mtx.unlock();
    cv.notify_all();
    std::this_thread::sleep_for(200ms);
    mtx.lock();
    q.push("message");
    mtx.unlock();
    cv.notify_all();
 }

void Consumer()
{
    std::cout << "Wait for notification." << std::endl;
    std::unique_lock<std::mutex> lck(mtx);
    if(q.empty())
        cv.wait(lck);
    std::string msg = q.front();
    q.pop();
    mtx.unlock();
    std::cout << "Get: " << msg <<std::endl;
}

int main()
{
    std::thread producer(Producer);
    std::thread consumer(Consumer);
    std::thread consumer2(Consumer);
    std::thread consumer3(Consumer);
    producer.join();
    consumer.join();
    consumer2.join();
    consumer3.join();
    return 0;
}

在这个例子中,有一个生产者线程与3个消费者线程,生产者线程第一次会生产2个message,但是notify_all在唤起所有3个消费者后,会有一个消费者获取不到message(最后获取锁的所有权的那个),这样就导致了那个线程被唤起后却无法获取元素。

Wait for notification.
Wait for notification.
Wait for notification.
Ready Send notification.
Get: message
Get: message
Get: 

操作系统引起的虚假唤起

当一个线程在wait情况下被唤起时,唤起他的并不是对应的条件变量信号(如pthread_cond_signal或notify_one/signal_one等方法)直接导致的。换句话说,即使没有对线程进行显式的唤醒操作,线程也可能因为某些原因(如操作系统的内部行为)被唤醒,但此时唤醒的条件并不满足,导致线程执行错误或不必要的操作。

  • 操作系统并不保证在调用唤醒函数(如pthread_cond_signal)时只唤醒一个或特定的线程。这可能导致多个线程同时被唤醒,而实际上可能只需要唤醒一个
    比方说一些来自于操作系统的中断信号,或者出现了另外一个并不与当前wait线程匹配的信号,也有可能将此wait线程唤起。

解决办法,判断标记值

#include <condition_variable>
#include <iostream>
#include <thread>
#include <queue>
#include <string>

using namespace std::chrono_literals;
std::condition_variable cv;
std::mutex mtx;
std::queue<std::string> q;

void Producer()
{
    std::this_thread::sleep_for(2000ms);
    std::cout << "Ready Send notification." << std::endl;
    mtx.lock();
    q.push("message");
    q.push("message");
    mtx.unlock();
    cv.notify_all();
    std::this_thread::sleep_for(200ms);
    mtx.lock();
    q.push("message");
    mtx.unlock();
    cv.notify_all();
 }

void Consumer()
{
    std::cout << "Wait for notification." << std::endl;
    std::unique_lock<std::mutex> lck(mtx);
    if(q.empty())
        cv.wait(lck,[]{return !q.empty();});
    std::string msg = q.front();
    q.pop();
    mtx.unlock();
    std::cout << "Get: " << msg <<std::endl;
}

int main()
{
    std::thread producer(Producer);
    std::thread consumer(Consumer);
    std::thread consumer2(Consumer);
    std::thread consumer3(Consumer);
    producer.join();
    consumer.join();
    consumer2.join();
    consumer3.join();
    return 0;
}

其中

cv.wait(lck,[]{return !q.empty();});

等价于

while(q.empty()){
    cv.wait(lck);
}

文章作者: xucanxx
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 xucanxx !
  目录