c – 用于大阵列的无复制线程安全环形缓冲区

对于大数组(10 ^ 7个元素)上的信号处理,我使用与环形缓冲区连接的不同线程.遗憾的是,只需要太多时间将数据复制到缓冲区和从缓冲区复制数据.当前实现基于boost :: lockfree :: spsc_queue.

因此,我正在寻找一种解决方案,通过将unique_ptr用于向量来交换线程和缓冲区之间向量的所有权(请参见附图:swapping pointer between threads and the queue).

移动智能指针并不符合我的需要,因为我需要在运行时不断为新的矢量元素分配内存.这个开销比复制数据要大.

我错过了那个设计的缺陷吗?

是否有线程安全甚至无锁的环形缓冲区实现允许推送和弹出的交换操作?

编辑:我修改了一个锁定环缓冲区来交换unique_ptr.性能提升很大.虽然它不是一个优雅的解决方案.有什么建议?

// https://github.com/embeddedartistry/embedded-resources/blob/master/examples/cpp/circular_buffer.cpp

#include <memory>
#include <mutex>

template <typename T, int SIZE>
class RingbufferPointer {
typedef std::unique_ptr<T> TPointer;
public:
    explicit RingbufferPointer() {
        // create objects
        for (int i=0; i<SIZE; i++) {
            buf_[i] = std::make_unique<T>();
        }
    }

    bool push(TPointer &item) {
        std::lock_guard<std::mutex> lock(mutex_);
        if (full())
            return false;

        std::swap(buf_[head_], item);

        if (full_)
            tail_ = (tail_ + 1) % max_size_;

        head_ = (head_ + 1) % max_size_;
        full_ = head_ == tail_;

        return true;
    }

    bool pop(TPointer &item) {
        std::lock_guard<std::mutex> lock(mutex_);
        if (empty())
            return false;

        std::swap(buf_[tail_], item);

        full_ = false;
        tail_ = (tail_ + 1) % max_size_;

        return true;
    }

    void reset() {
        std::lock_guard<std::mutex> lock(mutex_);
        head_ = tail_;
        full_ = false;
    }

    bool empty() const {
        return (!full_ && (head_ == tail_));
    }

    bool full() const {
        return full_;
    }

    int capacity() const {
        return max_size_;
    }

    int size() const {
        int size = max_size_;

        if(!full_) {
            if(head_ >= tail_)
                size = head_ - tail_;
            else
                size = max_size_ + head_ - tail_;
        }

        return size;
    }

private:
    TPointer buf_[SIZE];

    std::mutex mutex_;
    int head_ = 0;
    int tail_ = 0;
    const int max_size_ = SIZE;
    bool full_ = 0;
};

最佳答案

Moving smart pointers doesn’t fit my needs, because therefore I need
to allocate memory during runtime constantly for new vector elements.

如果您预先分配足够的存储空间并将自己的内存管理实现为简单的隔离存储,即a.k.a池,则不一定如此.

如果你这样做,没有什么可以阻止你交换,你可以使用支持交换元素的任何环形缓冲区来保持现有的体系结构,并保持与之前相同的线程安全性.您可以选中仅using boost::pool的选项,而不是实现自己的选项.

点赞