具有shutdown c ++的生产者Consumer Thread

时间:2013-07-02 05:51:16

标签: c++ producer-consumer

嗨,快速问题给出了以下c ++ 11代码,它适用于生产者/消费者,问题是我想关闭DataQueue并停止所有消费者。虽然存在问题,但问题在于消费者只是调用popWait()并且可以被阻止。在这种情况下如何关闭我的消费者?这可能是一个需要纠正的设计问题。我试图不产生任何性能命中,因为这个代码理想情况下应该使用破坏程序模式或类似的方法来使队列锁定免费。因为我可能想知道是否有一些简单的消费者知道在生产者关闭时停止调用pop等待功能。棘手的部分是关闭,如果队列中仍有数据,您必须等待消费者完成取消数据。我相信我有一个解决方案,消费者可以自己关机,但愿意接受。提前谢谢。

#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

3 个答案:

答案 0 :(得分:1)

一个想法是在调用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()方法,即没有将特殊的sentinel值填充到队列本身,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 :(得分:1)

您可以将“毒丸”对象推入队列。事实上,尽可能多地推动您想要关闭的消费者数量。这样,您还可以删除要关闭的成员变量,将此检查卸载到接收线程,这样可以提高整体排队性能。

这是我为线程池实现所做的方法,它完美地运行。

答案 2 :(得分:0)

popWait中,您可以检查队列是否为空或_shutDown是否为真。在后一种情况下,您通知调用者他不应再请求数据(抛出异常(在我看来不是最佳),或稍微重新设计方法签名)。然后,您只需在notifyAll()方法中进行shutdown()来电。