(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;
};