条件变量

2018-07-17  本文已影响0人  _gentle

条件遍历是一种同步机制,它阻塞线程直到满足某个条件,避免忙等待。C++11提供了两种条件变量,需要添加头文件#include<condition_variable>

  • condition_variable:配合std::unique_lock<std::mutex>进行wait操作
  • condition_variable_any: 和任意带有lock、unlock语义的mutex搭配使用,比较灵活,但效率比condition_variable差一些
#include<iostream>
#include<thread>
#include<mutex> 
#include<chrono>
#include<condition_variable>
#include<list> 
template<typename T>
class SyncQueue {
public:
    bool empty() {
        std::lock_guard<std::mutex> locker(m_mutex);
        return m_queue.empty();
    }
    bool full() {
        std::lock_guard<std::mutex> locker(m_mutex);
        return m_queue.size == m_maxSize();
    }
    SyncQueue(int maxSize):m_maxSize(maxSize){
    
    }
    
    void put(const T& x) {
        std::lock_guard<std::mutex> locker(m_mutex);
    //  while(isFull()){
//          std::cout << "the buffer is full" << std::endl;
//          m_notFull.wait(m_mutex); //等待临界区未满的条件 
//      }
        m_notFull.wait(m_mutex, [this]{return !isFull();});
        m_queue.push_back(x);
        m_notEmpty.notify_one();
    } 
    void Take(T& x) {
        std::lock_guard<std::mutex> locker(m_mutex);
    //  while(isEmpty()) {
        //  std::cout << "the buffer is empty" << std::endl; 
    //      m_notEmpty(m_mutex);//等待临界区非空的条件 
    //  }
        m_notEmpty.wait(m_mutex, [this]{return !isEmpty();}); //这种写法与上面的等价,但更简洁 
        x = m_queue.front();
        m_queue.pop_front();
        m_notFull.notify_one();// 唤醒一个在等待临界区未满的线程 
    }
    size_t Size() {
        std::lock_guard<std::mutex> locker(m_mutex);
        return m_queue.size(); 
    }
    int Count() {
        return m_queue.size();
    }
private:
    bool isFull() const {//这是一个非同步方法 
        return m_queue.size() == m_maxSize;
    }
    
    bool isEmpty() const {//这是一个非同步方法 
        return m_queue.empty();
    }
    std::list<T> m_queue;
    std::mutex m_mutex;
    std::condition_variable_any m_notEmpty;
    std::condition_variable_any m_notFull;
    int m_maxSize;
};

上面代码中wait会释放mutex,而lock_guard此时还拥有mutex,出现了语义不一致。如果此时线程结束,lock_guard析构会导致错误。

下面用condition_variable 和 unique_lock来实现同步队列。unique_lock可以随时释放锁(lock_guard析构的时候才会释放),因此wait函数中释放锁不会导致语义不一致

#include<iostream>
#include<thread>
#include<mutex> 
#include<chrono>
#include<condition_variable>
#include<list> 
template<typename T>
class SyncQueue {
public:
    bool empty() {
        std::lock_guard<std::mutex> locker(m_mutex);
        return m_queue.empty();
    }
    bool full() {
        std::lock_guard<std::mutex> locker(m_mutex);
        return m_queue.size == m_maxSize();
    }
    SyncQueue(int maxSize):m_maxSize(maxSize){
    
    }
    
    void put(const T& x) {
        std::unique_lock<std::mutex> locker(m_mutex);
        m_notFull.wait(m_mutex, [this]{return !isFull();});
        m_queue.push_back(x);
        m_notEmpty.notify_one();
    } 
    void Take(T& x) {
        std::unique_lock<std::mutex> locker(m_mutex);
        m_notEmpty.wait(m_mutex, [this]{return !isEmpty();}); //这种写法与上面的等价,但更简洁 
        x = m_queue.front();
        m_queue.pop_front();
        m_notFull.notify_one();// 唤醒一个在等待临界区未满的线程 
    }
    size_t Size() {
        std::lock_guard<std::mutex> locker(m_mutex);
        return m_queue.size(); 
    }
    int Count() {
        return m_queue.size();
    }
private:
    bool isFull() const {//这是一个非同步方法 
        return m_queue.size() == m_maxSize;
    }
    
    bool isEmpty() const {//这是一个非同步方法 
        return m_queue.empty();
    }
    std::list<T> m_queue;
    std::mutex m_mutex;
    std::condition_variable m_notEmpty;
    std::condition_variable m_notFull;
    int m_maxSize;
};
上一篇下一篇

猜你喜欢

热点阅读