我在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)
之前被调用,因此它&#34;未命中&#34; notify_all()
。这会发生吗?
如果这是问题所在,解决问题的最佳方法是什么?
答案 0 :(得分:1)
由于SafeQueue对象由生产者拥有,因此删除生产者会导致被通知的消费者与生产者完成时被删除的SafeQueue之间的竞争条件。
我建议共享资源既不是生产者也不是消费者,而是作为对每个资源的构造函数的引用传递。
更改Producer和Consumer构造函数;
Producer( SafeQueue<Packet> & queue ) :
_running(false), _incoming_packets(queue) {}
Consumer( SafeQueue<Packet> & queue ) :
_running(false), _incoming_packets(queue) {}
以这种方式使用您的实例;
SafeQueue<Packet> queue;
Producer producer(queue);
Consumer consumer(queue);
...do stuff...
queue.shutdown();
这也解决了您在Consumer类中与Producer类紧密耦合的不良设计问题。
此外,在析构函数中杀死和连接线程可能是一个坏主意,就像你为~Producer所做的那样。最好为每个线程类添加Shutdown()方法,并显式调用它们;
producer.shutdown();
consumer.shutdown();
queue.shutdown();
关闭顺序并不重要,除非您担心在停止使用者时丢失仍在队列中的未处理数据包。
答案 1 :(得分:0)
在您的 return Ok(myResult); // gives emphasis on status, my personal favorite
return new OkObjectResult(myResult); // for me a little bit verbose and the same
// effect as Ok; but states we return an Object
return new ObjectResult(myResult); // gives emphasis of the content that is returned
中,您可能以错误的方式使用SafeQueue::Dequeue
...更改此信息:
std::condition_variable
到
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 Dequeue(T& item)
{
std::unique_lock<std::mutex> lock(_queue_mutex);
_condition_variable.wait(lock, []{ return _shutdown || !_queue.empty() });
if(!_shutdown)
{
item = std::move(_queue.front());
_queue.pop();
return true;
}
return false;
}
中数据成员的初始化顺序与构造函数的关系不正确
Consumer
应重新订购:
class Consumer
{
Consumer(Producer* producer) :
_producer(producer),
_t(std::bind(&Consumer::WorkerThread, this))
{ }
......
// _t will be constructed first, regardless of your constructor initializer list
// Meaning, the thread can even start running using an unintialized _producer
std::thread _t;
Producer* _producer;
}
问题的另一部分由CAB's answer
涵盖