条件变量与虚假唤醒

1. 条件变量

假若现在存在两个线程A与线程B,线程B向线程A发送一个信号时,线程A才开始向下执行。
std::condition_variable实际上是一个类,是一个和条件相关的一个类,等待一个条件达成,才能执行相关操作;这个类是需要和互斥量来配合工作的,用的时候我们要生成这个类的对象;

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <condition_variable>

using namespace std;
class Condition{
public:
    Condition(){}

    void producerMsg(){
        int i=0;
        while(i<m_count)
        {
            i++;
            cout<<"添加数据:"<<i<<endl;
            unique_lock<std::mutex> guard(m_mutex);
            m_msg.push_back(i);
            guard.unlock();         //提前解锁,唤醒另一个线程时,此线程互斥量已解锁;
            m_cond.notify_one();
        }
    }
    void consumerMsg()
    {
        int i=0;
        while(true)
        {
            unique_lock<std::mutex> guard(m_mutex);
            //解锁互斥量,并阻塞到本行,等待notify_xx;

            /*
                 m_cond.wait(guard);    //具有虚假唤醒的风险,导致数据顺序不是自己希望的循序;
            */
            /*
            while (m_msg.empty()) {
                m_cond.wait(guard);     // 为了防止虚假唤醒;也可采用下面方法;
            }
            */

            /*
            m_cond.wait(guard,[this](){
                if(!m_msg.empty())
                    return true;
                else {
                    return false;
                }
            });
            */

            //是上式的简化
            m_cond.wait(guard,[this](){
                return !this->m_msg.empty();
            });
            cout<<"取出数据:"<<m_msg.back()<<endl;
            m_msg.pop_back();
            guard.unlock();
            cout<<"现在还有数据:"<<m_msg.size()<<endl;
            this_thread::sleep_for(chrono::milliseconds(5));
            i++;
            if(i>=m_count)
            {
                break;
            }
        }

    }
    int size(){
        return m_msg.size();
    }
private:
    vector<int> m_msg;
    std::mutex m_mutex;
    std::condition_variable m_cond;

    int m_count=1000;//数据量大小
};

int main()
{
    Condition con;
    thread consumer(&Condition::consumerMsg,&con);
    thread producer(&Condition::producerMsg,&con);
    consumer.join();
    producer.join();
    cout<<"------------------------------------"<<endl;
    cout<<"程序执行完毕之后剩余数据:"<<con.size()<<endl;
    return 0;
}

2. 虚假唤醒

首先看维基百科上的解释:spurious wake up
A spurious wake up happens when a thread wakes up from waiting on a condition variable that’s been signaled, only to discover that the condition it was waiting for isn’t satisfied. It’s called spurious because the thread has seemingly been awakened for no reason. But spurious wake-ups don’t happen for no reason: they usually happen because, in between the time when the condition variable was signaled and when the waiting thread finally ran, another thread ran and changed the condition. There was a race condition between the
threads, with the typical result that sometimes, the thread waking up on the condition variable runs first, winning the race, and sometimes it runs second, losing the race.

On many systems, especially multiprocessor systems, the problem of spurious wake-ups is exacerbated because if there are several threads waiting on the condition variable when it’s signaled, the system may decide to wake them all up, treating every signal( )to wake one thread as a broadcast( )to wake all of them, thus breaking any possibly expected 1:1 relationship between signals and wake-ups. [1] If there are ten threads waiting, only one will win and the other nine will experience spurious wake-ups.

To allow for implementation flexibility in dealing with error conditions and races inside the operating system, condition variables may also be allowed to return from a wait even if not signaled, though it is not clear how many implementations actually do that. In the Solaris implementation of condition variables, a spurious wake-up may occur without the condition being signaled if the process is signaled; the wait system call aborts and returns EINTR .The Linux pthread implementation of condition variables guarantees it will not do that.

Because spurious wake-ups can happen whenever there’s a race and possibly even in the absence of a race or a signal, when a thread wakes on a condition variable, it should always check that the condition it sought is satisfied. If it’s not, it should go back to sleeping on the
condition variable, waiting for another opportunity.

即:由于线程之间存在竞争(race),对于线程发送的notify_xx信号,获得锁的线程可以继续执行下去,对于没有获得锁的线程,则继续wait,等待下一次notify_xx信号,而对于这些需要再次进入等待的线程而言,此次唤醒就是spurious wake up😭notify_xx:(notify_one()/notify_all()));

3.解决方法:

			/*
                 m_cond.wait(guard);    //具有虚假唤醒的风险,导致数据顺序不是自己希望的循序;
            */
            /*
            while (m_msg.empty()) {
                m_cond.wait(guard);     // 为了防止虚假唤醒;也可采用下面方法;
            }
            */

            /*
            m_cond.wait(guard,[this](){
                if(!m_msg.empty())
                    return true;
                else {
                    return false;
                }
            });
            */

            //是上式的简化
            m_cond.wait(guard,[this](){
                return !this->m_msg.empty();
            });

如有错误请指出,谢谢;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值