是否可以实现由数组组成的线程安全的圆形bufffer?

时间:2019-05-08 20:56:33

标签: c++ thread-safety circular-buffer

我正在尝试实现一个利用互斥量的循环缓冲区,以确保线程安全。我一直在使用以下代码:

#include <cstdio>

#include <memory>
#include <mutex>

template <class T>
class circular_buffer {
public:
    explicit circular_buffer(size_t size) :
        buf_(std::unique_ptr<T[]>(new T[size])),
        max_size_(size)
    {

    }

    void put(T item)
    {
        std::lock_guard<std::mutex> lock(mutex_);

        buf_[head_] = item;

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

        head_ = (head_ + 1) % max_size_;

        full_ = head_ == tail_;
    }

    T get()
    {
        std::lock_guard<std::mutex> lock(mutex_);

        if (empty())
        {
            return T();
        }

        //Read data and advance the tail (we now have a free space)
        auto val = buf_[tail_];
        full_ = false;
        tail_ = (tail_ + 1) % max_size_;

        return val;
    }

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

    bool empty() const
    {
        //if head and tail are equal, we are empty
        return (!full_ && (head_ == tail_));
    }

    bool full() const
    {
        //If tail is ahead the head by 1, we are full
        return full_;
    }

    size_t capacity() const
    {
        return max_size_;
    }

    size_t size() const
    {
        size_t size = max_size_;

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

        return size;
    }

private:
    std::mutex mutex_;
    std::unique_ptr<T[]> buf_;
    size_t head_ = 0;
    size_t tail_ = 0;
    const size_t max_size_;
    bool full_ = 0;
};

此代码的问题是,我似乎无法使它与浮点数数组一起使用。我得到一个函数返回数组错误(从get函数)。我不完全确定如何解决此问题(尝试传入数组并使用get()函数指向该数组,但这也不起作用)。抱歉,如果这个问题有点抽象,那么我真的完全不愿意(我是开发人员的第一份工作,实际上是我工作了6天,他们让我开发了一个非常复杂的雷达绘图应用程序)。让我知道您是否需要任何澄清。

edit:谢谢大家!迈克尔的回答很奏效,感谢您的建议。老实说,我现在好像被淹死了,所以所有提示都非常有用!

1 个答案:

答案 0 :(得分:4)

首先,请注意,如果有人同时使用此类模板的实例的size()empty()full()方法,而其他人同时使用{{1} },get()put(),最终将导致未定义的行为。 reset()size()也将必须锁定互斥锁,因为它们读取可能被修改的对象(empty()full_head_)的值同时。除此之外,在我看来tail_总是写一些东西,即使队列已满。那可能不是人们通常想要的。

根据您的描述,我认为您要询问的问题与尝试创建put()有关。考虑一下如果将circular_buffer<float[4]>的类型替换为get()float[4]方法会变成什么:

T

最后得到一个返回数组的函数。函数不允许返回数组[dcl.fct]/11。*这就是为什么一旦在这样的float get()[4] { … } 上调用get()方法就会导致编译器错误的原因。使用例如std::array代替:circular_buffer

*)我认为这很可能是出于历史原因。数组类型被设计为在传递给C中的函数时表现的方式是,使得数组最终有效地被引用传递。函数没有按引用返回数组的好方法,而按值返回将与它们的传递方式不一致。因此,最好是根本不允许返回数组……