是否可以实现由数组组成的线程安全循环缓冲区?
Is it possible to implement a thread safe circular bufffer that consists of arrays?
我正在尝试实现一个循环缓冲区,该缓冲区利用互斥锁来保证线程安全。我一直在使用以下代码:
#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;
};
这段代码的问题是我似乎无法让它与浮点数组一起工作。我收到函数 returns 数组错误(来自 get 函数)。我不完全确定如何解决这个问题(尝试传入一个数组并使用 get() 函数指向该数组,但这也不起作用)。抱歉,如果这个问题有点抽象,老实说,我对这个问题完全不知所措(作为开发人员的第一份工作,实际上是我工作的 6 天,他们让我制作了一个非常复杂的雷达测绘应用程序)。如果您需要任何说明,请告诉我。
编辑:谢谢大家! Michael 的回答有效,感谢您的建议。老实说,我现在感觉自己快被淹没了,所以所有的提示都非常有用!
首先,请注意,如果有人使用此 class 模板实例的 size()
、empty()
或 full()
方法,而其他人正在同时使用 get()
、put()
或 reset()
,您最终会出现未定义的行为。 size()
或 empty()
也必须锁定互斥量,因为它们读取可能被修改的对象(full_
、head_
和 tail_
)的值同时。除此之外,在我看来 put()
总是写一些东西,即使队列已满。这可能不是人们通常想要的。
根据您的描述,我认为您询问的问题与尝试创建有关,例如 circular_buffer<float[4]>
。想想如果用类型 float[4]
代替 T
:
,get()
方法会变成什么
float get()[4] { … }
你最终得到一个函数 returning 数组。函数不允许 return 数组 [dcl.fct]/11.* That's why you end up with a compiler error as soon as you'd call the get()
method on such a circular_buffer
. Use, e.g., std::array
而是:circular_buffer<std::array<float, 4>>
.
*) 我认为这很可能是出于历史原因。数组类型被设计为在传递给 C 中的函数时的行为方式使得数组最终将有效地通过引用传递;函数通过引用 return 数组没有好的方法,并且通过值 returning 将与它们的传入方式不一致。因此,最好只是禁止数组是 return 完全没有……
我正在尝试实现一个循环缓冲区,该缓冲区利用互斥锁来保证线程安全。我一直在使用以下代码:
#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;
};
这段代码的问题是我似乎无法让它与浮点数组一起工作。我收到函数 returns 数组错误(来自 get 函数)。我不完全确定如何解决这个问题(尝试传入一个数组并使用 get() 函数指向该数组,但这也不起作用)。抱歉,如果这个问题有点抽象,老实说,我对这个问题完全不知所措(作为开发人员的第一份工作,实际上是我工作的 6 天,他们让我制作了一个非常复杂的雷达测绘应用程序)。如果您需要任何说明,请告诉我。
编辑:谢谢大家! Michael 的回答有效,感谢您的建议。老实说,我现在感觉自己快被淹没了,所以所有的提示都非常有用!
首先,请注意,如果有人使用此 class 模板实例的 size()
、empty()
或 full()
方法,而其他人正在同时使用 get()
、put()
或 reset()
,您最终会出现未定义的行为。 size()
或 empty()
也必须锁定互斥量,因为它们读取可能被修改的对象(full_
、head_
和 tail_
)的值同时。除此之外,在我看来 put()
总是写一些东西,即使队列已满。这可能不是人们通常想要的。
根据您的描述,我认为您询问的问题与尝试创建有关,例如 circular_buffer<float[4]>
。想想如果用类型 float[4]
代替 T
:
get()
方法会变成什么
float get()[4] { … }
你最终得到一个函数 returning 数组。函数不允许 return 数组 [dcl.fct]/11.* That's why you end up with a compiler error as soon as you'd call the get()
method on such a circular_buffer
. Use, e.g., std::array
而是:circular_buffer<std::array<float, 4>>
.
*) 我认为这很可能是出于历史原因。数组类型被设计为在传递给 C 中的函数时的行为方式使得数组最终将有效地通过引用传递;函数通过引用 return 数组没有好的方法,并且通过值 returning 将与它们的传入方式不一致。因此,最好只是禁止数组是 return 完全没有……