C++;相当于Java';s阻塞队列 我正在把一些java代码移植到C++中,一个特定的部分利用阻塞队列将消息从许多生产者传递给单个消费者。

C++;相当于Java';s阻塞队列 我正在把一些java代码移植到C++中,一个特定的部分利用阻塞队列将消息从许多生产者传递给单个消费者。,c++,multithreading,C++,Multithreading,如果您不熟悉Java BlockingQueue是什么,那么它只是一个具有硬容量的队列,它公开了线程安全的方法来从队列中put()和take()。如果队列已满,则put()阻塞;如果队列为空,则take()阻塞。此外,还提供了这些方法的超时敏感版本 超时与我的用例相关,因此提供超时的建议是理想的。如果没有,我可以自己编写一些代码 我在谷歌上搜索了一下,快速浏览了Boost库,但没有找到类似的东西。也许我是瞎子……但有人知道一个好的推荐吗 谢谢> P>不是固定大小,它不支持超时,但这是最近我用C+

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

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

我在谷歌上搜索了一下,快速浏览了Boost库,但没有找到类似的东西。也许我是瞎子……但有人知道一个好的推荐吗


谢谢

> P>不是固定大小,它不支持超时,但这是最近我用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;
    }
};
#包括
#包括
#包括
模板
类队列
{
私人:
std::mutex d_mutex;
std::条件变量d_条件;
std::dequed_队列;
公众:
无效推送(T常数和值){
{
std::unique_lock锁(此->d_互斥锁);
d_队列。向前推_(值);
}
此->d_条件。通知_one();
}
T pop(){
std::unique_lock锁(此->d_互斥锁);
this->d_条件.wait(锁,[=]{return!this->d_队列.empty();});
trc(std::move(this->d_queue.back());
此->d_队列。弹出返回();
返回rc;
}
};
扩展和使用定时等待弹出应该很简单。我没有这么做的主要原因是,我对目前为止想到的界面选择不满意。

下面是一个功能示例:

模板类阻塞队列{
std::条件变量cvCanPop;
std::互斥同步;
std::队列qu;
bool _bShutdown=false;
公众:
无效推送(常数T和项目)
{
{
std::唯一锁定(同步);
_推(项);
}
_cvCanPop.notify_one();
}
void RequestShutdown(){
{
std::唯一锁定(同步);
_bshutton=true;
}
_cvCanPop.notify_all();
}
bool Pop(T和项目){
std::唯一锁定(同步);
对于(;;){
if(_qu.empty()){
如果(关闭){
返回false;
}
}
否则{
打破
}
_cvCanPop.wait(锁);
}
item=std::move(_qu.front());
_qu.pop();
返回true;
}
};

好吧,我来晚了一点,但我认为这更适合Java的
阻塞队列
实现。在这里,我也使用一个互斥和两个条件来处理not full和not empty。IMO a
阻塞队列
在容量有限的情况下更有意义,这是我在其他答案中没有看到的。我还包括一个简单的测试场景:

#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;
}
#包括
#包括
#包括
#包括
#包括
#包括
模板
类阻塞队列{
私人:
容量大小;
std::queue\u queue;
std::mutex\u mutex;
std::条件变量未满;
std::条件变量不为空;
公众:
内联阻塞队列(大小容量):\u容量(容量){
//空的
}
内联大小\u t size()常量{
std::唯一的锁(互斥锁);
返回_queue.size();
}
内联bool empty()常量{
std::唯一的锁(互斥锁);
return_queue.empty();
}
内联无效推送(常量T&elem){
{
std::唯一的锁(互斥锁);
//等待队列已满
而(_queue.size()>=_容量){
_未满。等待(锁定);
}

std::cout手工制作的类,它有一个数组(可能是deque而不是数组,以便更轻松地向前推后)互斥锁呢?硬容量真的是一个要求吗?在我的例子中,是的。很可能生产者的速度超过消费者,我需要阻止生产者端的线程,或者以其他方式拒绝他们的输入,以免我耗尽内存!推送中的作用域是必要的吗?我想你是在试图解锁互斥锁…但我不确定是不是有必要notify_one的要求。
push()中的范围
是不必要的,但如果没有它,则在锁仍然保持的情况下会发出条件变量的信号。在发出信号之前释放锁可以使锁随时可用。@javapowered:考虑到使用了锁,这并不重要!可能对可以等待锁的线程有限制,但我无法想象这会发生什么相关。使用d_u或m_u作为成员变量的前缀是一个风格问题,还是在这个选择中有一些语义?@Isaac:这是一个风格问题。在不同的组织中,成员变量使用了不同的指标。在不同的组织中,我什么都没有使用,a
m_u
-前缀,a
d_
-前缀,a
-后缀和一个[不明智的]
\uuu
-前缀。目前,我在一个使用
d\u
-前缀的组织中工作。即使STL队列也是这样工作的(您必须使用front()访问第一个元素,然后使用pop()访问它),我认为这种接口在多线程场景中不能很好地工作,因为您无法在一个操作中弹出第一个元素并获取其值,您可以删除一个不同的元素,而不是您刚刚获得的元素。或者我在这里遗漏了什么吗?非常正确,它可以很容易地扩展为执行
pop2
frontAndPop
,将这些元素融合为一个元素,然后涵盖需要原子线程安全访问而不是两步方法的用例。
#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;
}