【发布时间】:2016-09-15 13:36:12
【问题描述】:
我在 C++ 中使用这个类来设置生产者-消费者:
#pragma once
#include <queue>
#include <mutex>
#include <condition_variable>
#include <memory>
#include <atomic>
template <typename T> class SafeQueue
{
public:
SafeQueue() :
_shutdown(false)
{
}
void Enqueue(T item)
{
std::unique_lock<std::mutex> lock(_queue_mutex);
bool was_empty = _queue.empty();
_queue.push(std::move(item));
lock.unlock();
if (was_empty)
_condition_variable.notify_one();
}
bool Dequeue(T& item)
{
std::unique_lock<std::mutex> lock(_queue_mutex);
while (!_shutdown && _queue.empty())
_condition_variable.wait(lock);
if(!_shutdown)
{
item = std::move(_queue.front());
_queue.pop();
return true;
}
return false;
}
bool IsEmpty()
{
std::lock_guard<std::mutex> lock(_queue_mutex);
return _queue.empty();
}
void Shutdown()
{
_shutdown = true;
_condition_variable.notify_all();
}
private:
std::mutex _queue_mutex;
std::condition_variable _condition_variable;
std::queue<T> _queue;
std::atomic<bool> _shutdown;
};
我是这样使用它的:
class Producer
{
public:
Producer() :
_running(true),
_t(std::bind(&Producer::ProduceThread, this))
{ }
~Producer()
{
_running = false;
_incoming_packets.Shutdown();
_t.join();
}
SafeQueue<Packet> _incoming_packets;
private:
void ProduceThread()
{
while(_running)
{
Packet p = GetNewPacket();
_incoming_packets.Enqueue(p);
}
}
std::atomic<bool> _running;
std::thread _t;
}
class Consumer
{
Consumer(Producer* producer) :
_producer(producer),
_t(std::bind(&Consumer::WorkerThread, this))
{ }
~Consumer()
{
_t.join();
}
private:
void WorkerThread()
{
Packet p;
while(producer->_incoming_packets.Dequeue(p))
ProcessPacket(p);
}
std::thread _t;
Producer* _producer;
}
这大部分都有效。但是偶尔当我删除生产者(并导致它的解构器调用SafeQueue::Shutdown,_t.join() 永远阻塞。
我的猜测是问题出在此处(SafeQueue::Dequeue):
while (!_shutdown && _queue.empty())
_condition_variable.wait(lock);
来自线程#1 的SafeQueue::Shutdown 在线程#2 完成检查_shutdown 时被调用,但在它执行_condition_variable.wait(lock) 之前,它“错过”了notify_all()。这会发生吗?
如果这是问题所在,最好的解决方法是什么?
【问题讨论】:
-
您是否将警告设置为最高级别?您的 Consumer 类中有一个微妙的错误。您的数据成员的顺序和您打算在构造函数中初始化它们的顺序冲突......检查它。线程将在分配
_producer之前创建 -
你再次错误地使用了条件变量......它不应该在这样的while循环中。它有一个允许进行此类测试的重载
-
@WhiZTiM 我知道它有这样的重载,但它相当于一个 while 循环:en.cppreference.com/w/cpp/thread/condition_variable/wait。
-
@WhiZTiM 我不完全理解这是如何导致活锁的,也许我在这里遗漏了一些明显的东西...... std::condition_variable::wait: @987654333 @(来自en.cppreference.com/w/cpp/thread/condition_variable/wait)
-
这会发生吗?是的。 notify_all 不保证线程上下文切换。消费者可以在等待调用中,~生产者会销毁队列。消费者指向生产者的指针现在无效。队列之前占用的内存无效。现在未定义激活消费者线程时等待代码的行为方式。
标签: c++ multithreading concurrency queue