Я использую этот класс для установки производителя-потребитель в C++: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);
SafeQueue::Shutdown
от нити # 1 вызывается в то время как поток # 2 По окончании проверки _shutdown, но прежде чем он выполнен _condition_variable.wait(lock)
, поэтому он «промахивается» notify_all()
. Может ли это случиться?
Если это проблема, что это лучший способ ее решить?
Включите ли вы свои предупреждения на максимальном уровне? У вас тонкая ошибка в вашем классе Consumer. Порядок ваших членов данных и порядок, который вы собираетесь инициализировать в конфликтах вашего конструктора ... Проверьте его. Нить будет создана до того, как назначен '_producer' – WhiZTiM
Опять вы используете неверную переменную условия ... Ее не должно быть в тактическом цикле. У этого есть перегрузка, которая допускает такое тестирование. – WhiZTiM
@WhiZTiM Я знаю, что он имеет такую перегрузку, но это эквивалентно циклу while: http://en.cppreference.com/w/cpp/thread/condition_variable/wait. – UnTraDe