C++的線程安全的隊列

(1)在衆多實現方法中這個實現兼顧了易讀性、性能和功能。

(2)這個實現要求構建工具支持C++11的atomic mutex condition_veriable功能。這是C++11的基礎特性,一般2011年以後的C++編譯器都能支持。 例如,visual studio 2012以上。

(3)這個類的實現中有兩處使用了unique_lock而不是lock_guard,這是data_cond.wait所需要的,unique_lock是lock_guard的增強版(也意味着比lock_guard佔用多一些的資源和運行時間)。

(4)通過std::move的使用(前提是我們實現的類型T定義了移動構造函數和移動賦值函數),能利用移動語義帶來的性能優勢。這個類在處理一個元素的過程中,比較耗時的只有:創建一個數據對象(make_shared)和使用互斥鎖的,這個容器性能還是比較高的,即使以毫秒級的頻率使用本隊列也不會對應用的運行時間造成實質的影響。

(5)使用shared_ptr<T>返回元素,用戶無需釋放元素的內存。

(6)有一個地方不容易講清楚,但不講又是一個隱患。成員方法termination()中的lock_guard<mutex> lk(mut);是必須的。因爲成員方法wait_and_pop中的data_cond.wait(lk,    [this]{return ((!data_queue.empty())需要與termination()互斥執行。wait的實現實際是:while(!Pred())wait(Lck);Pred()代表本里中的lambda表示式,Lck代表本例中的變量lk。如果不存在上述的互斥,A線程在剛剛執行完Pred(),B線程執行termination();重新調度A線程時,A線程無法感應到影響lambda表達式結果的條件已經變化,根據之前的Pred()結果所以調用了wait(Lck);。可是B線程已經執行過termination();不會再次調用喚醒阻塞的data_cond.notify_all(),造成A一直阻塞.

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

using namespace std;

template<typename T>
class threadsafe_queue
{
public:
	threadsafe_queue()
	{
		m_bTermination = false;
	}
	~threadsafe_queue(void)
	{

	}
	//(1)沒有調用termination時,每調用一次出隊一個元素,直到隊列爲空本方法阻塞線程。
	//(2)在調用了termination後,本方法永不阻塞,如果原本已經處於阻塞狀態,接觸阻塞狀態。
	//(3)返回true時,value值有效。返回false時,value值無效。調用了termination且隊列爲空時返回false.
	bool wait_and_pop(T& value)
	{
		unique_lock<mutex> lk(mut);
		data_cond.wait(lk,[this]{return ((!data_queue.empty())||m_bTermination);});
		//不爲空則出隊
		if (!data_queue.empty())
		{
			value = move(*data_queue.front());
			data_queue.pop();
			return true;
		}
		//隊列爲空則返回失敗
		return false;
	}

	//隊列爲空返回false
	bool try_pop(T& value)
	{
		lock_guard<mutex> lk(mut);
		if (data_queue.empty())
		{
			return false;
		}
		value = move(*data_queue.front());
		data_queue.pop();
		return true;
	}
	std::shared_ptr<T> wait_and_pop()
	{
		unique_lock<mutex> lk(mut);
		data_cond.wait(lk,[this]{return ((!data_queue.empty())||m_bTermination);});
		if (!data_queue.empty())
		{
			shared_ptr<T> res = data_queue.front();
			data_queue.pop();
			return res;
		}
		return nullptr;
	}
	//隊列爲空返回null
	std::shared_ptr<T> try_pop()
	{
		lock_guard<mutex> lk(mut);
		if(data_queue.empty())
		{
			return nullptr;
		}
		shared_ptr<T> res = data_queue.front();
		data_queue.pop();
		return res;
	}
	//插入一項
	void push(T new_value)
	{
		if (m_bTermination)
		{
			return;
		}
		shared_ptr<T> data(make_shared<T>(move(new_value)));
		lock_guard<mutex> lk(mut);
		data_queue.push(data);
		data_cond.notify_one();
	}
	bool empty() const
	{
		lock_guard<mutex> lk(mut);
		return data_queue.empty();
	}
	int size()
	{
		lock_guard<mutex> lk(mut);
		return data_queue.size();
	}
	//設置隊列爲退出狀態。在退出狀態下,忽略入隊,可以執行出隊,但當隊列爲空時,wait_and_pop不會阻塞。
	void termination()
	{
                lock_guard<mutex> lk(mut);
		m_bTermination = true;
		data_cond.notify_all();
	}
	//是退出狀態嗎
	bool is_termination()
	{
		return m_bTermination;
	}
private:
	mutable mutex mut;
	queue<shared_ptr<T>> data_queue;
	condition_variable data_cond;
	atomic<bool> m_bTermination;
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章