Привет быстрый вопрос, учитывая следующий код C ++ 11, который прекрасно работает для производителя / потребителя, проблема в том, что я хочу закрыть DataQueue и остановить всех потребителей. Хотя проблема заключается в том, что проблема заключается в том, что потребители просто вызывают popWait () и могут быть заблокированы. Как я могу отключить своих потребителей в этом случае? Вероятно, это проблема дизайна, которую необходимо устранить. Я стараюсь не снижать производительность, так как в этом коде в идеале должен использоваться шаблон прерывателя или что-то подобное, чтобы освободить очередь блокировки. Как бы то ни было, мне было интересно, было ли что-то простое, чтобы потребители знали, чтобы перестать вызывать функцию ожидания поп-музыки, когда производитель отключается. Сложная часть отключается, если в очереди все еще есть данные, которые вам придется подождать, чтобы потребители завершили сбор данных. Я считаю, что у меня есть решение, при котором у потребителя есть свое отключение, но оно открыто для идей. Заранее спасибо.
#ifndef __DataQueue_h__
#define __DataQueue_h__
#include <mutex>
#include <queue>
#include <condition_variable>
#include <chrono>
template <typename DataT>
class DataQueue
{
public:
DataQueue (): _shutdown(false), _waitTime(5), _itemAvailable() {}
void push ( const DataT& data )
{
std::unique_lock<std::mutex> lock(_mutex);
queue.push(data);
_itemAvailable.notify_one();
}
// worked fine until I need to shutdown services... then some were blocked
DataT popWait()
{
std::unique_lock<std::mutex> lock(_mutex);
if(queue.empty())
{
_itemAvailable.wait(lock);
}
DataT temp(queue.front());
queue.pop();
return temp;
}
inline void shutdown()
{
_shutdown = true;
}
private:
std::queue<DataT> queue;
bool _shutdown;
unsigned int _waitTime;
std::mutex _mutex;
std::condition_variable _itemAvailable;
};
#endif
Одна идея состояла бы в том, чтобы разбудить всех потребителей в призыве к shutdown
1. в popWait
метод, вы могли бы затем проверить, был ли установлен флаг выключения, когда вы вернетесь из wait
,
#include <atomic>
#include <mutex>
#include <queue>
#include <condition_variable>
#include <chrono>
template <typename DataT>
class DataQueue
{
public:
DataQueue (): _shutdown(false), _itemAvailable() {}
void push ( const DataT& data )
{
std::unique_lock<std::mutex> lock(_mutex);
queue.push(data);
_itemAvailable.notify_one();
}
Maybe<DataT> popWait()
{
std::unique_lock<std::mutex> lock(_mutex);
while(queue.empty() && !_shutdown)
{
_itemAvailable.wait(lock);
}
Maybe<DataT> data;
// leave pending data in the queue
if (_shutdown)
{
// consumers should stop polling when receiving an 'empty' value
return data;
}
data.add(queue.front());
queue.pop();
return data;
}
inline void shutdown()
{
_shutdown = true;
_itemAvailable.notify_all();
}
private:
std::queue<DataT> queue;
std::atomic<bool> _shutdown;
std::mutex _mutex;
std::condition_variable _itemAvailable;
};
Возвращаемое значение popWait
Помимо всего, что связано с синхронизацией и сигнализацией, вы также должны пересмотреть возвращаемое значение popWait
, Если вы хотите реализовать общий shutdown()
метод, т. е. без помещения специальных дозорных значений в саму очередь, popWait
должен иметь возможность возвращать «значение», которое указывает, что производитель остановился — возможно, что-то вроде шаблона Maybe<DataT>
2. Я предполагаю, что Maybe<DataT>
мог либо вернуться DataT
или ничего, в этом случае потребитель прекратил бы опрос.
template<typename DataT>
class Maybe
{
DataT _data;
bool _empty;
pulic:
Maybe() : _data(), _empty(true) {};
void add(const DataT& raData)
{
_data=raData;
_empty=false;
}
bool isEmpty() const
{
return _empty;
}
DataT get() const
{
return _data;
}
}
Это довольно примитивный интерфейс. Вы можете расширить его по мере необходимости.
1 ComicSansMS указал мне, что я должен объявить _shutdown
переменная-член как std::atmic<bool>
чтобы избежать проблем с переупорядочением памяти. Спасибо за хедз-ап.
2Я просто споткнулся std::optional<T>
(новое в C ++ 14), что по сути то, что я имел в виду.
Вы можете поместить объект «отравленная таблетка» в очередь. Фактически, выдвиньте столько потребителей, сколько вы хотите отключить. Таким образом, вы также избавитесь от проверяемой переменной-члена shutdown, разгрузив эту проверку в принимающий поток, что повысит общую производительность очереди.
Это подход, который я использовал для своей реализации пула потоков, и он отлично работает.
В popWait
Вы можете проверить, если очередь пуста ИЛИ _shutDown
правда. В последнем случае вы уведомляете вызывающего абонента о том, что он больше не должен запрашивать данные (генерирует исключение (на мой взгляд, не оптимально) или немного изменяет сигнатуру метода). Тогда все, что вам нужно сделать, это иметь notifyAll()
позвоните в ваш shutdown()
метод.