【问题标题】:Why doesn't boost::lockfree::spsc_queue have emplace?为什么 boost::lockfree::spsc_queue 没有 emplace?
【发布时间】:2015-07-14 22:26:44
【问题描述】:

常规的std::vectoremplace_back,可以避免不必要的复制。 spsc_queue 不支持这个有什么原因吗?是否因为某种原因无法使用无锁队列进行emplace

【问题讨论】:

    标签: boost thread-safety locking emplace


    【解决方案1】:

    我既不是 boost 库的实现者也不是维护者,所以我不知道为什么不包含 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 调用的单个生产者)并获取适当的下一个索引, 检查以确保所有内容都在边界内(通过加载获取以与调用 pop 的线程进行适当同步),但我们感兴趣的主要语句是:

    new (buffer + write_index) T(t); // copy-construct
    

    它执行将新副本构造放置到缓冲区中。传递一些参数以用于直接从可行的构造函数参数构造T,这在本质上是线程不安全的。我编写了以下 sn-p 并在派生类中进行了必要的更改,以适当地将工作委托给基类:

    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 来代替复制构造。

    【讨论】:

      猜你喜欢
      • 2017-10-23
      • 1970-01-01
      • 2015-06-22
      • 1970-01-01
      • 2015-01-09
      • 1970-01-01
      • 2014-04-24
      • 1970-01-01
      相关资源
      最近更新 更多