常规std::vector
有emplace_back
,可以避免不必要的副本。有没有理由spsc_queue
不支持这个?由于某种原因,无法使用无锁队列进行emplace
吗?
答案 0 :(得分:4)
我不是一个提升库实现者,也不是维护者,因此我不知道为什么不包含emplace
成员函数的理由,但是自己实现它并不困难。如果你真的需要它。
spsc_queue
的基类为compile_time_sized_ringbuffer
或runtime_sized_ringbuffer
,具体取决于编译时是否知道队列的大小。这两个类使用动态缓冲区和编译时缓冲区之间的明显差异来维护实际缓冲区,但在这种情况下,将它们的push
成员函数委托给公共基类 - ringbuffer_base
。
ringbuffer_base::push
函数相对容易理解:
bool push(T const & t, T * buffer, size_t max_size)
{
const size_t write_index = write_index_.load(memory_order_relaxed); // only written from push thread
const size_t next = next_index(write_index, max_size);
if (next == read_index_.load(memory_order_acquire))
return false; /* ringbuffer is full */
new (buffer + write_index) T(t); // copy-construct
write_index_.store(next, memory_order_release);
return true;
}
应该存储下一个项目的位置的索引使用relaxed
加载(这是安全的,因为此类的预期用途是push
调用的单个生成器)并获取适当的下一个索引,检查以确保所有内容都是入站的(使用load-acquire与调用pop
的线程进行适当的同步),但我们感兴趣的主要声明是:
new (buffer + write_index) T(t); // copy-construct
执行将新副本构造放置到缓冲区中。关于传递一些参数以用于直接从可行的构造函数参数构造T
,没有什么本质上线程不安全的。我编写了以下代码片段,并在派生类中进行了必要的更改,以便将工作适当地委派给基类:
template<typename ... Args>
std::enable_if_t<std::is_constructible<T,Args...>::value,bool>
emplace( T * buffer, size_t max_size,Args&&... args)
{
const size_t write_index = write_index_.load(memory_order_relaxed); // only written from push thread
const size_t next = next_index(write_index, max_size);
if (next == read_index_.load(memory_order_acquire))
return false; /* ringbuffer is full */
new (buffer + write_index) T(std::forward<Args>(args)...); // emplace
write_index_.store(next, memory_order_release);
return true;
}
也许唯一的区别是确保Args...
中传递的参数实际上可用于构建T
,当然还可以通过std::forward
进行安置而不是复制构造