C ++相当于Java的BlockingQueue

时间:2012-10-09 17:13:51

标签: c++ multithreading

我正在将一些Java代码移植到C ++,一个特定部分使用BlockingQueue将消息从多个生产者传递给单个消费者。

如果您不熟悉Java BlockingQueue是什么,它只是一个具有硬容量的队列,它将线程安全方法暴露给队列中的put()和take()。如果队列已满,则put()阻塞;如果队列为空,则使用take()块。此外,还提供了这些方法的超时敏感版本。

超时与我的用例相关,因此提供这些超时的建议是理想的。如果没有,我可以自己编写代码。

我已经google了一下,并迅速浏览了Boost库,我找不到这样的东西。也许我在这里失明......但是有人知道一个好建议吗?

谢谢!

4 个答案:

答案 0 :(得分:41)

它不是固定大小,它不支持超时,但这是我最近使用C ++ 2011构造发布的队列的简单实现:

#include <mutex>
#include <condition_variable>
#include <deque>

template <typename T>
class queue
{
private:
    std::mutex              d_mutex;
    std::condition_variable d_condition;
    std::deque<T>           d_queue;
public:
    void push(T const& value) {
        {
            std::unique_lock<std::mutex> lock(this->d_mutex);
            d_queue.push_front(value);
        }
        this->d_condition.notify_one();
    }
    T pop() {
        std::unique_lock<std::mutex> lock(this->d_mutex);
        this->d_condition.wait(lock, [=]{ return !this->d_queue.empty(); });
        T rc(std::move(this->d_queue.back()));
        this->d_queue.pop_back();
        return rc;
    }
};

扩展和使用定时等待弹出应该是微不足道的。我没有做到的主要原因是我对目前为止我想到的界面选择不满意。

答案 1 :(得分:1)

以下是blocking queue with shutdown request功能的示例:

template <typename T> class BlockingQueue {
  std::condition_variable _cvCanPop;
  std::mutex _sync;
  std::queue<T> _qu;
  bool _bShutdown = false;

public:
  void Push(const T& item)
  {
    {
      std::unique_lock<std::mutex> lock(_sync);
      _qu.push(item);
    }
    _cvCanPop.notify_one();
  }

  void RequestShutdown() {
    {
      std::unique_lock<std::mutex> lock(_sync);
      _bShutdown = true;
    }
    _cvCanPop.notify_all();
  }

  bool Pop(T &item) {
    std::unique_lock<std::mutex> lock(_sync);
    for (;;) {
      if (_qu.empty()) {
        if (_bShutdown) {
          return false;
        }
      }
      else {
        break;
      }
      _cvCanPop.wait(lock);
    }
    item = std::move(_qu.front());
    _qu.pop();
    return true;
  }
};

答案 2 :(得分:0)

BlockingCollection是一个C ++ 11线程安全集合类,它以.NET BlockingCollection类为模型。

它支持以下内容:

  • 经典生产者/消费者模式(即条件变量,互斥体)的实现
  • 同时从多个线程中添加和获取项目。
  • 可选的最大容量。
  • 插入和删除操作会在集合为空或已满时阻塞。
  • 插入和删除“ try”操作不会阻塞或阻塞最多 指定的时间段。
  • 插入和删除“批量”操作,允许一次添加或采用多个元素。
  • FIFO,LIFO,基于优先级的插入和删除操作。
  • 通过管理生产者线程和使用者线程的活动子集,最大程度地减少睡眠,唤醒和锁定争用。
  • 基于范围的循环支持。

答案 3 :(得分:0)

好的,我来晚了一点,但是我认为这更适合Java的BlockingQueue实现。在这里,我也使用一个互斥锁和两个条件来照顾未满和不为空的情况。 IMO BlockingQueue在容量有限的情况下更有意义,而我在其他答案中没有看到。我也提供了一个简单的测试方案:

#include <iostream>
#include <algorithm>
#include <queue>
#include <mutex>
#include <thread>
#include <condition_variable>

template<typename T>
class blocking_queue {
private:
    size_t _capacity;
    std::queue<T> _queue;
    std::mutex _mutex;
    std::condition_variable _not_full;
    std::condition_variable _not_empty;

public:
    inline blocking_queue(size_t capacity) : _capacity(capacity) {
        // empty
    }

    inline size_t size() const {
        std::unique_lock<std::mutex> lock(_mutex);
        return _queue.size();
    }

    inline bool empty() const {
        std::unique_lock<std::mutex> lock(_mutex);
        return _queue.empty();
    }

    inline void push(const T& elem) {
        {
            std::unique_lock<std::mutex> lock(_mutex);

            // wait while the queue is full
            while (_queue.size() >= _capacity) {
                _not_full.wait(lock);
            }
            std::cout << "pushing element " << elem << std::endl;
            _queue.push(elem);
        }
        _not_empty.notify_all();
    }

    inline void pop() {
        {
            std::unique_lock<std::mutex> lock(_mutex);

            // wait while the queue is empty
            while (_queue.size() == 0) {
                _not_empty.wait(lock);
            }
            std::cout << "popping element " << _queue.front() << std::endl;
            _queue.pop();
        }
        _not_full.notify_one();
    }

    inline const T& front() {
        std::unique_lock<std::mutex> lock(_mutex);

        // wait while the queue is empty
        while (_queue.size() == 0) {
            _not_empty.wait(lock);
        }
        return _queue.front();
    }
};

int main() {
    blocking_queue<int> queue(5);

    // create producers
    std::vector<std::thread> producers;
    for (int i = 0; i < 10; i++) {
        producers.push_back(std::thread([&queue, i]() {
            queue.push(i);
            // produces too fast
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }));
    }

    // create consumers
    std::vector<std::thread> consumers;
    for (int i = 0; i < 10; i++) {
        producers.push_back(std::thread([&queue, i]() {
            queue.pop();
            // consumes too slowly
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }));
    }

    std::for_each(producers.begin(), producers.end(), [](std::thread &thread) {
        thread.join();
    });

    std::for_each(consumers.begin(), consumers.end(), [](std::thread &thread) {
        thread.join();
    });

    return EXIT_SUCCESS;
}