Производитель Consumer Thread с отключением переполнения стека

Привет быстрый вопрос, учитывая следующий код 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

1

Решение

Одна идея состояла бы в том, чтобы разбудить всех потребителей в призыве к shutdown1. в 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), что по сути то, что я имел в виду.

1

Другие решения

Вы можете поместить объект «отравленная таблетка» в очередь. Фактически, выдвиньте столько потребителей, сколько вы хотите отключить. Таким образом, вы также избавитесь от проверяемой переменной-члена shutdown, разгрузив эту проверку в принимающий поток, что повысит общую производительность очереди.

Это подход, который я использовал для своей реализации пула потоков, и он отлично работает.

1

В popWait Вы можете проверить, если очередь пуста ИЛИ _shutDown правда. В последнем случае вы уведомляете вызывающего абонента о том, что он больше не должен запрашивать данные (генерирует исключение (на мой взгляд, не оптимально) или немного изменяет сигнатуру метода). Тогда все, что вам нужно сделать, это иметь notifyAll() позвоните в ваш shutdown() метод.

0
По вопросам рекламы [email protected]