C++11 条件变量

一、条件变量简介
1、 线程同步指线程间按照预定的先后次序进行的行为。

2、 互斥量用来保护多线程同时访问的共享数据,保证共享数据被安全访问。

3、 条件变量是一种线程间通信机制,使线程实现"等待"–>"唤醒"的逻辑,用于在线程之间传递消息。

4、 单靠互斥量无法实现线程同步,C++11提供条件变量结合互斥量,实现线程同步。

5、 多线程间共享条件变量,当条件不满足时,相关线程被一直阻塞(当前线程wait),直到条件出现,这些线程才会被唤醒(其它线程nofity)。

6、 使用条件变量,需包含头文件

7、 条件变量的取名,就按照所需要的条件来命名,作为前提条件放在具体操作的前面。条件变量没有具体的值,只有单纯的等待和唤醒。

8、 条件变量的wait()函数需要互斥锁作为参数,在wait()函数里有获取/释放mutex的操作。

二、C++11提供两种条件变量
1、 一种condition_variable,另一种condition_variable_any。

2、 condition_variable,配合std::mutex使用。

3、 condition_variable_any,配合任意带有lock()、unlock()的mutex使用。

4、 condition_variable_any更灵活,condition_variable性能更好。

三、条件变量成员函数
1、 无参构造。构造对象。没有拷贝构造和赋值操作。

2、 析构。析构对象。

3、 notify_one()。通知一个等待的线程。如果没有线程等待,则函数不执行任何操作; 如果等待的线程超过一个,则唤醒的线程不确定,看调度。

4、 notify_all()。通知所有等待的线程。如果没有线程等待,则函数不执行任何操作。

5、 带参wait。阻塞当前线程,直到条件变量被唤醒。

6、 带参wait_for。阻塞当前线程,直到条件变量被唤醒,或到指定时间段后。

7、 带参wait_util。阻塞当前线程,直到条件变量被唤醒,或抵达指定时间点后。

四、条件变量的使用过程:
1、 拥有条件变量的线程获取互斥量。

2、 循环检查某个条件,如果条件不满足,则阻塞直到条件满足;如果条件满足,则向下执行。

3、 某个线程满足条件执行完之后调用notify_one或notify_all唤醒一个或者所有的等待线程。

五、注意wait参数:
1、 wait函数有如下两个重载:_Lck是互斥锁,_Pred是条件判断式。
void wait(_Lock & _Lck);
void wait(_Lock & _Lck, _Predicate _Pred);
前者将条件判断写在外面,后者将条件判断写在里面,二者效果一样,例如下面两段代码效果一样:
(1)、

   {
       std::unique_lock locker(m_mutex);
       while(IsFull()) //防止虚假唤醒
       {
           m_notFull.wait(locker);
       }
   }

(2)、

   {
       std::unique_lock locker(m_mutex);
       m_notFull.wait(locker, [this]{return !IsFull();});  //条件判断式可以防止虚假唤醒。
   }

2、 带条件的wait,如果条件变量被唤醒(wait时被唤醒)或刚进入wait()执行,则先检查判断式是否满足条件。
如果满足条件,则重新获取mutex(刚进入wait()执行时mutex已被获取),然后结束wait(),继续往下执行;
如果不满足条件,则释放mutex,然后将线程置为waiting状态,继续等待。

3、 总结:条件变量真正结束wait()继续往下执行,必须:
(1)、被notify或刚进入wait()执行;
(2)、条件满足;
(3)、mutex获取成功。

4、 虚假唤醒:某线程A的条件变量正在wait,由于未知原因被其它线程notify,此时线程A被唤醒,但此时线程A并未满足继续执行的条件,这就叫虚假唤醒。

5、 通常用循环条件判断或wait的条件判断式来防止虚假唤醒。

六、代码举例:同步队列

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;


template 
class SimpleSyncQueue
{
public:
    SimpleSyncQueue() {}

    void Put(const T & x)
    {
        std::unique_lock locker(m_mutex);
        m_queue.push_back(x);
        m_notEmpty.notify_one();
    }

    void Take(T & x)
    {
        std::unique_lock locker(m_mutex);
        m_notEmpty.wait(locker, [this] {return !m_queue.empty(); });
        x = m_queue.front();
        m_queue.pop_front();
    }

    bool Empty()
    {
        std::lock_guard locker(m_mutex);
        return m_queue.empty();
    }

    int Size()
    {
        std::lock_guard locker(m_mutex);
        return m_queue.size();
    }

private:
    std::list m_queue;
    std::mutex m_mutex;
    std::condition_variable m_notEmpty;
};

你可能感兴趣的:(c++,开发语言)