为什么boost :: lockfree :: spsc_queue没有升级?

时间:2015-07-14 22:26:44

标签: boost thread-safety locking emplace

常规std::vectoremplace_back,可以避免不必要的副本。有没有理由spsc_queue不支持这个?由于某种原因,无法使用无锁队列进行emplace吗?

1 个答案:

答案 0 :(得分:4)

我不是一个提升库实现者,也不是维护者,因此我不知道为什么不包含emplace成员函数的理由,但是自己实现它并不困难。如果你真的需要它。

spsc_queue的基类为compile_time_sized_ringbufferruntime_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进行安置而不是复制构造