另一个线程安全队列实现
Posted
技术标签:
【中文标题】另一个线程安全队列实现【英文标题】:Another thread safe queue implementation 【发布时间】:2012-12-19 22:31:04 【问题描述】:我有一个班级Queue
,我试图让线程安全。它有这三个成员变量:
std::queue<T> m_queue;
pthread_mutex_t m_mutex;
pthread_cond_t m_condition;
一个推送和弹出实现为:
template<class T> void Queue<T>::push(T value)
pthread_mutex_lock( &m_mutex );
m_queue.push(value);
if( !m_queue.empty() )
pthread_cond_signal( &m_condition );
pthread_mutex_unlock( &m_mutex );
template<class T> bool Queue<T>::pop(T& value, bool block)
bool rtn = false;
pthread_mutex_lock( &m_mutex );
if( block )
while( m_queue.empty() )
pthread_cond_wait( &m_condition, &m_mutex );
if( !m_queue.empty() )
value = m_queue.front();
m_queue.pop();
rtn = true;
pthread_mutex_unlock( &m_mutex );
return rtn;
不幸的是,偶尔会出现可能是此代码错误的问题。也就是说,有两个线程,有时线程 1 永远不会从 push()
出来,而在其他时候线程 2 永远不会从 pop()
出来(block
参数是 true
)尽管队列不为空。
我知道还有其他可用的实现,但如果需要,我想尝试修复此代码。有人发现有什么问题吗?
构造函数有适当的初始化:
Queue()
pthread_mutex_init( &m_mutex, NULL );
pthread_cond_init( &m_condition, NULL );
和析构函数,对应的'destroy'调用。
【问题讨论】:
您是否在像gdb
这样的调试器中运行它? push()
或 pop()
是否在中间抛出异常/崩溃?
很抱歉,但是你的代码没有任何错误……而且写得很好……尝试用gdb调试,但问题不在你看的地方跨度>
次要修复:在推送功能中if( !m_queue.empty() )
始终为真
block
在哪里设置/取消设置?
您应该使用 RAII 来调用锁定/解锁。否则异常会导致锁出现问题。这可能并不明显,但存在潜在的异常情况(特别是如果 T 不简单)。
【参考方案1】:
正如 Paul Rubel 所说,您需要先初始化互斥锁。在这一点上,将互斥锁包装在一个将为您处理初始化和终结的类中可能是一个好主意。例如:
class mutex
private:
mutex(const mutex &m);
mutex &operator=(const mutex &m);
// OR inherit from boost::noncopyable
public:
mutex()
pthread_mutex_init(&mut_, nullptr);
~mutex()
pthread_mutex_destroy(&mut_);
pthread_mutex_t get() const return mut_;
private:
pthread_mutex_t mut_;
;
值得注意的是 Boost.Threading 库,其中包含编写得非常好的同步类。
【讨论】:
还有std::mutex 类,如果你有幸使用了一个符合 C++11 的工具链和一个完善的 thread support library。 @WhozCraig 要是我们都这么幸运就好了! @anthony-arnold:对于您的课程,您应该删除复制构造函数和赋值运算符,否则您违反了三规则。【参考方案2】:您必须在使用前初始化互斥锁:pthread_mutex_init
//somewhere before push/pop
pthread_mutex_init(&m_mutex)
【讨论】:
感谢您的帮助,尽管我已经这样做了。我只是没有在我的帖子中表现得那么完整。【参考方案3】:这与你的问题完全无关,但你可以修复推送功能:
template<class T> void Queue<T>::push(T value)
pthread_mutex_lock( &m_mutex );
if( m_queue.empty() )
m_queue.push(value);
pthread_cond_signal( &m_condition );
else
m_queue.push(value);
pthread_mutex_unlock( &m_mutex );
【讨论】:
或者只是if(m_queue.empty()) pthread_cond_signal(&m_condition); m_queue.push(value);
因为这都是在锁内,所以在推送值之前发出信号是可以的。
看来我可以把if(m_queue.empty())
全部取出来了。谢谢。【参考方案4】:
我意识到在为 ARM 测试构建时出现了问题。解决方案是更新 pthreads 库。使用更新的 pthread,一切运行良好。
【讨论】:
以上是关于另一个线程安全队列实现的主要内容,如果未能解决你的问题,请参考以下文章
基于event 实现的线程安全的优先队列(python实现)