Можно ли реализовать потокобезопасный круговой буфер, состоящий из массивов? - PullRequest
1 голос
/ 08 мая 2019

Я пытаюсь реализовать кольцевой буфер, который использует мьютекс, чтобы быть потокобезопасным.Я использовал следующий код:

#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 Ответ

4 голосов
/ 09 мая 2019

Прежде всего, имейте в виду, что если кто-либо использует методы size(), empty() или full() экземпляра этого шаблона класса, в то время как кто-то другой одновременно использует get(), put() или reset(), вы получите неопределенное поведение. size() или empty() также придется блокировать мьютекс, потому что они считывают значения объектов (full_, head_ и tail_), которые потенциально могут быть изменены одновременно. Кроме того, мне кажется, что put() всегда что-то пишет, даже если очередь заполнена. Это, вероятно, не то, что обычно хочется.

Исходя из вашего описания, я предполагаю, что проблема, о которой вы спрашиваете, связана с попыткой создать, например, circular_buffer<float[4]>. Подумайте, во что превратится метод get(), если вы замените тип float[4] на T:

float get()[4] { … }

В результате вы получаете функцию, возвращающую массив. Функциям не разрешено возвращать массивы [dcl.fct] / 11 . * Вот почему вы заканчиваете с ошибкой компилятора, как только вы вызываете метод get() для такого circular_buffer. Используйте, например, std::array вместо: circular_buffer<std::array<float, 4>>.

*) Я считаю, что это наиболее вероятно по историческим причинам. Типы массивов, предназначенные для поведения при передаче функциям в C, были таковы, что массивы эффективно передавались по ссылке; у функции нет хорошего способа вернуть массив по ссылке, и возвращение по значению будет несовместимо с тем, как они передаются. Поэтому, вероятно, лучше всего вообще запретить возвращать массивы & hellip;

...