C++ 循环缓冲区的线程安全实现

C++ 循环缓冲区的线程安全实现,c++,multithreading,boost,opencv,C++,Multithreading,Boost,Opencv,boost库中的循环缓冲区不是线程安全的。所以我将boost::circular\u buffer对象包装在一个类中,如下所示。线程之间的互斥(我认为)是通过使用条件变量、互斥和锁获取/释放来实现的。这个实现是线程安全的吗 #include <boost/thread/condition.hpp> #include <boost/thread/mutex.hpp> #include <boost/thread/thread.hpp> #include <

boost库中的循环缓冲区不是线程安全的。所以我将boost::circular\u buffer对象包装在一个类中,如下所示。线程之间的互斥(我认为)是通过使用条件变量、互斥和锁获取/释放来实现的。这个实现是线程安全的吗

#include <boost/thread/condition.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include <boost/circular_buffer.hpp>

// Thread safe circular buffer 
template <typename T>
class circ_buffer : private boost::noncopyable
{
public:
    typedef boost::mutex::scoped_lock lock;
    circ_buffer() {}
    circ_buffer(int n) {cb.set_capacity(n);}
    void send (T imdata) {
        lock lk(monitor);
        cb.push_back(imdata);
        buffer_not_empty.notify_one();
    }
    T receive() {
        lock lk(monitor);
        while (cb.empty())
            buffer_not_empty.wait(lk);
        T imdata = cb.front();
        cb.pop_front();
        return imdata;
    }
    void clear() {
        lock lk(monitor);
        cb.clear();
    }
    int size() {
        lock lk(monitor);
        return cb.size();
    }
    void set_capacity(int capacity) {
        lock lk(monitor);
        cb.set_capacity(capacity);
    }
private:
    boost::condition buffer_not_empty;
    boost::mutex monitor;
    boost::circular_buffer<T> cb;
};
#包括
#包括
#包括
#包括
//线程安全循环缓冲区
模板
类循环缓冲区:私有boost::不可复制
{
公众:
typedef boost::mutex::作用域锁定;
循环缓冲区(){}
循环缓冲区(int n){cb.set_容量(n);}
无效发送(IMT数据){
锁lk(监视器);
cb.推回(imdata);
缓冲区不为空。通知一个();
}
不接收(){
锁lk(监视器);
while(cb.empty())
缓冲区不为空。等待(lk);
T imdata=cb.front();
cb.pop_front();
返回imdata;
}
无效清除(){
锁lk(监视器);
cb.clear();
}
int size(){
锁lk(监视器);
返回cb.size();
}
空集容量(整数容量){
锁lk(监视器);
cb.设置容量(容量);
}
私人:
boost::条件缓冲区不为空;
互斥监视器;
boost::循环缓冲区cb;
};
Edit这现在是一个模板类,它接受任何类型的对象(不仅仅是
cv::Mat
对象)

是。
如果使用同一个锁锁定所有公共方法,那么它将是线程安全的

你可以考虑使用<强> <强>,如果你有很多并发读者,它可能会有更好的性能。


如果您没有很多读卡器,这只会增加开销,但可能值得检查该选项并进行测试。

乍看起来不错,只是您根本没有使用
缓冲区未满的情况。您可能希望添加类似于
缓冲区的代码,而不是空的
代码。

我认为它看起来不错,只是在
发送中有一些无意义的Mat副本。您不需要新的,您可以直接将
send
参数推送到您的cb。

您的实现与下面所示的实现类似。您应该阅读该博客,看看您的实现中是否遗漏了任何内容

如果
Mat
对象的创建/复制成本很高,则应避免连续创建/复制/删除它们。相反,您应该拥有一个Mat对象池(也称为自由列表),这些对象在某种管道体系结构中不断得到回收。我在本文中描述了这种类型的体系结构,并回答了一个相关的问题

在这个答案中,我建议使用阻塞堆栈来实现池,但也可以使用阻塞
循环\u缓冲区
。我之所以建议使用堆栈,是因为我认为它可能对缓存更友好,但我从未实际测量过它是否会产生影响。

非常古老的问题:) 下面是一个带有无锁实现的disgin


这里有一个BSD-2库,它看起来更适合这个

。请原谅我的愚蠢问题,但是哪里需要线程安全的循环缓冲区?在我使用过循环缓冲区的所有地方,像这样从多个线程访问它都是一个严重的错误。所以出于好奇,你的用例是什么?@LiKao我用它从网络摄像头抓取帧到MATLAB中,见我之前的帖子。你会怎么做?你的Mat对象有多大/多贵?@LiKao:你会用这样的东西来实现生产者-消费者队列()。这样的队列可以在多线程管道中的各个阶段之间使用。如果数据源产生的数据超出缓冲区的容量,boost::circular\u buffer对象将覆盖最旧的数据。这没关系。所以不需要检查
buffer\u not\u full
条件。我认为读写锁在循环缓冲区中没有意义。生产者和消费者都修改了缓冲区,所以他们实际上都是作者。@DavidRodríguez dribeas-在这个例子中你是对的。我没有真正进入设计,只是线程安全部分。+1用于避免无意义、昂贵和争用性增加的复制。@MartinJames I无法直接推动send的论点。“cv::Mat类实现引用计数和浅复制,这样当一个图像被分配给另一个图像时,图像数据不会被复制,并且两个图像将指向同一个内存块。”“保留引用计数,以便只有当对映像的所有引用都被破坏时,内存才会被释放。如果您希望创建包含原始图像新副本的图像,您将使用“OpenCV 2计算机视觉应用程序编程手册”(第28页)中的copyTo()方法。您仍然不需要新的。在这种情况下,在堆栈上分配新映像也可以。但这不是您想要的功能吗?循环缓冲区中的共享副本?+1让我意识到我不需要在堆上分配Mat。Mat对象可以使用
Mat image2;
image.copy存储在堆栈上To(image2);
然后
cb.push_back(image2);
。我仍然需要使用copyTo()因为否则,缓冲区中的所有对象都将引用上次发送的对象。或者,我可以将图像复制移动到我的circ_buffer类之外,在这种情况下,是的,我可以按照您的建议将send参数直接推送到缓冲区。'当一个图像分配给另一个图像时,图像数据不会被复制,并且两个图像都将指向到同一内存块“-这不正是您想要的吗?当您将引用分配给队列时,不进行复制,现在有2个引用。当在生产者中创建新映像并且覆盖旧映像指针时,队列上只有一个引用。因此,不需要复制。Mat对象(映像)不是太大,每次打电话都差不多。我只是