【发布时间】:2022-01-25 07:38:48
【问题描述】:
我正在努力让自己了解 C++11 的最新变化,并且我正在围绕 std::queue 创建一个名为 SafeQueue 的线程安全包装器。我有两个可能阻塞的条件,队列已满和队列为空。我为此使用 std::condition_variable 。不幸的是,在 Linux 上,对我的空状态的 notify_all() 调用是段错误的。它在带有 clang 的 Mac 上运行良好。这是 enqueue() 方法中的段错误:
#ifndef mqueue_hpp
#define mqueue_hpp
#include <queue>
#include <mutex>
//////////////////////////////////////////////////////////////////////
// SafeQueue - A thread-safe templated queue. //
//////////////////////////////////////////////////////////////////////
template<class T>
class SafeQueue
{
public:
// Instantiate a new queue. 0 maxsize means unlimited.
SafeQueue(unsigned int maxsize = 0);
~SafeQueue(void);
// Enqueue a new T. If enqueue would cause it to exceed maxsize,
// block until there is room on the queue.
void enqueue(const T& item);
// Dequeue a new T and return it. If the queue is empty, wait on it
// until it is not empty.
T& dequeue(void);
// Return size of the queue.
size_t size(void);
// Return the maxsize of the queue.
size_t maxsize(void) const;
private:
std::mutex m_mutex;
std::condition_variable m_empty;
std::condition_variable m_full;
std::queue<T> m_queue;
size_t m_maxsize;
};
template<class T>
SafeQueue<T>::SafeQueue(unsigned int maxsize) : m_maxsize(maxsize) { }
template<class T>
SafeQueue<T>::~SafeQueue() { }
template<class T>
void SafeQueue<T>::enqueue(const T& item) {
// Synchronize.
if ((m_maxsize != 0) && (size() == m_maxsize)) {
// Queue full. Can't push more on. Block until there's room.
std::unique_lock<std::mutex> lock(m_mutex);
m_full.wait(lock);
}
{
std::lock_guard<std::mutex> lock(m_mutex);
// Add to m_queue and notify the reader if it's waiting.
m_queue.push(item);
}
m_empty.notify_all();
}
template<class T>
T& SafeQueue<T>::dequeue(void) {
// Synchronize. No unlock needed due to unique lock.
if (size() == 0) {
// Wait until something is put on it.
std::unique_lock<std::mutex> lock(m_mutex);
m_empty.wait(lock);
}
std::lock_guard<std::mutex> lock(m_mutex);
// Pull the item off and notify writer if it's waiting on full cond.
T& item = m_queue.front();
m_queue.pop();
m_full.notify_all();
return item;
}
template<class T>
size_t SafeQueue<T>::size(void) {
std::lock_guard<std::mutex> lock(m_mutex);
return m_queue.size();
}
template<class T>
size_t SafeQueue<T>::maxsize(void) const {
return m_maxsize;
}
#endif /* mqueue_hpp */
显然我做错了什么,但我无法弄清楚。 gdb 的输出:
Core was generated by `./test'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x0000000000000000 in ?? ()
(gdb) bt
#0 0x0000000000000000 in ?? ()
#1 0x0000000000414739 in std::condition_variable::notify_all() ()
#2 0x00000000004054c4 in SafeQueue<int>::enqueue (this=0x7ffee06b3470,
item=@0x7ffee06b355c: 1) at ../mqueue.hpp:59
#3 0x0000000000404ab6 in testsafequeue () at test.cpp:13
#4 0x0000000000404e99 in main () at test.cpp:49
(gdb) frame 2
#2 0x00000000004054c4 in SafeQueue<int>::enqueue (this=0x7ffee06b3470,
item=@0x7ffee06b355c: 1) at ../mqueue.hpp:59
59 m_empty.notify_all();
(gdb) info locals
No locals.
(gdb) this.m_empty
Undefined command: "this.m_empty". Try "help".
(gdb) print this->m_empty
$1 = {_M_cond = {__data = {{__wseq = 0, __wseq32 = {__low = 0,
__high = 0}}, {__g1_start = 0, __g1_start32 = {__low = 0,
__high = 0}}, __g_refs = {0, 0}, __g_size = {0, 0},
__g1_orig_size = 0, __wrefs = 0, __g_signals = {0, 0}},
__size = '\000' <repeats 47 times>, __align = 0}}
帮助表示赞赏。
我的示例测试崩溃了。
SafeQueue<int> queue(10);
queue.enqueue(1);
enqueue() 中 notify_all() 的段错误。
【问题讨论】:
-
在你的实现中肯定存在数据竞争:
enqueue()函数应该只获取一次锁并检查它需要检查的任何内容,如果必须的话,可能会阻塞条件变量。但是,这些只是实现上的语义错误,它们不应该导致崩溃。我猜崩溃是由这个类的使用方式引起的。 -
你应该删除你的复制/移动ctor/赋值操作符。
-
@o11c 它们已经被隐式定义为已删除或根本没有隐式声明。
-
我在上面展示了如何使用实现。这很简单。