1. 程式人生 > 程式設計 >c++11 多執行緒程式設計——如何實現執行緒安全佇列

c++11 多執行緒程式設計——如何實現執行緒安全佇列

執行緒安全佇列的介面檔案如下:

#include <memory> 
template<typename T>
class threadsafe_queue {
 public:
  threadsafe_queue();
  threadsafe_queue(const threadsafe_queue&);
  threadsafe_queue& operator=(const threadsafe_queue&) = delete;

  void push(T new_value);

  bool try_pop(T& value);
  std::shared_ptr<T> try_pop();

  void wait_and_pop(T& value);
  std::shared_ptr<T> wait_and_pop();

  bool empty() const;
};

push函式

push()函式實現向佇列新增資料的功能。新增資料後,使用std::condition_variable的notify_one通知取資料時被阻塞的執行緒。

void push(T tValue) {
  std::shared_ptr<T> data(std::make_shared<T>(std::move(tValue)));
  std::lock_guard<std::mutex> lk(mut);
  data_queue.push(data);
  data_con.notify_one();
}

wait_and_pop函式

wait_and_pop()函式實現從佇列取資料的功能,當佇列為空時,執行緒被掛起,等待有資料時被喚醒。

注意,這兩個函式中沒有使用std::lock_guard,而是使用std::unique_lock,這是為什麼呢?

這是因為std::condition_variable的wait函式會首先檢測條件data_queue.empty()是否滿足,如果佇列為空,wait函式會釋放mutex,並被掛起;當有新的資料進入佇列,std::condition_variable的wait函式會被喚醒,重新嘗試獲取mutex,然後檢測佇列是否為空,如果佇列非空,則繼續向下執行。由於函式的執行過程存在鎖的釋放和重新獲取,所以沒有使用std::lock_guard,而是選擇了std::unique_lock。

void wait_and_pop(T& value) {
  std::unique_lock<std::mutex> lk(mut);
  data_cond.wait(lk,[this]{return !data_queue.empty();});
  value=data_queue.front();
  data_queue.pop();
}

std::shared_ptr<T> wait_and_pop() {
  std::unique_lock<std::mutex> lk(mut);
  data_cond.wait(lk,[this]{return !data_queue.empty();});
  std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
  data_queue.pop();
  return res;
}

try_pop函式

try_pop函式提供非阻塞呼叫下的彈出佇列(queue)的功能。彈出成功返回true或者非空shared_ptr,失敗則返回false或者nullptr。

bool try_pop(T& value) {
  std::lock_guard<std::mutex> lk(mut);
  if(data_queue.empty()) {
    return false;
  } 
  value = data_queue.front();
  data_queue.pop();
  
  return true;
}

std::shared_ptr<T> try_pop() {
  std::lock_guard<std::mutex> lk(mut);
  if(data_queue.empty()) {
    return std::shared_ptr<T>();
  }
  std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
  data_queue.pop();
  return res;
}

empty函式

bool empty() const {
  std::lock_guard<std::mutex> lk(mut);
  return data_queue.empty();
}

這裡注意,empty()是const型別的成員函式,表明它宣告自己並不改變任何成員變數,但是mutex lock是一個mutating opertation,所以必須要將mut宣告為mutable型別(mutable std::mutex mut)。

完整程式碼如下:

#include <queue>
#include <memory>
#include <mutex>
#include <condition_variable>

template<typename T>
class threadsafe_queue {
 private:
   mutable std::mutex mut; 
   std::queue<T> data_queue;
   std::condition_variable data_cond;
 public:
   threadsafe_queue(){}
   threadsafe_queue(threadsafe_queue const& other) {
     std::lock_guard<std::mutex> lk(other.mut);
     data_queue=other.data_queue;
   }

   void push(T new_value) {
     std::lock_guard<std::mutex> lk(mut);
     data_queue.push(new_value);
     data_cond.notify_one();
   }

   void wait_and_pop(T& value) {
     std::unique_lock<std::mutex> lk(mut);
     data_cond.wait(lk,[this]{return !data_queue.empty();});
     value=data_queue.front();
     data_queue.pop();
   }

   std::shared_ptr<T> wait_and_pop() {
     std::unique_lock<std::mutex> lk(mut);
     data_cond.wait(lk,[this]{return !data_queue.empty();});
     std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
     data_queue.pop();
     return res;
   }

   bool try_pop(T& value) {
     std::lock_guard<std::mutex> lk(mut);
     if(data_queue.empty())
       return false;
     value=data_queue.front();
     data_queue.pop();
     return true;
   }

   std::shared_ptr<T> try_pop() {
     std::lock_guard<std::mutex> lk(mut);
     if(data_queue.empty())
       return std::shared_ptr<T>();
     std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
     data_queue.pop();
     return res;
   }

   bool empty() const {
     std::lock_guard<std::mutex> lk(mut);
     return data_queue.empty();
   }
};

以上就是c++ 如何實現執行緒安全佇列的詳細內容,更多關於c++ 執行緒安全佇列的資料請關注我們其它相關文章!