【问题标题】:Shared buffer using boost::intrusive_ptr使用 boost::intrusive_ptr 的共享缓冲区
【发布时间】:2018-06-08 14:57:40
【问题描述】:

我有一个用例,其中一个线程将消息读取到一个大缓冲区中,然后将处理分配给一堆线程。之后,缓冲区由多个线程共享。它是只读的,当最后一个线程完成时,必须释放缓冲区。缓冲区是从无锁平板分配器分配的。

我最初的设计是使用 shared_ptr 作为缓冲区。但是缓冲区可以有不同的大小。我的解决方法是做这样的事情。

struct SharedBuffer {
    SharedBuffer (uint16_t len, std::shared_ptr<void> ptr)
                 : _length(len), _buf(std::move(ptr))
    {
    }
    uint8_t data () { return (uint8_t *)_buf.get(); }
    uint16_t length 
    std::shared_ptr<void> _buf; // type-erase the shared_ptr as the SharedBuffer
                                // need to stored in some other structs
};

现在分配器将像这样分配 shared_ptr:

SharedBuffer allocate (size_t size)
{
     auto buf = std::allocate_shared<std::array<uint8_t, 16_K>>(myallocator);
     return SharedBuffer{16_K, buf}; // type erase the std::array
}

SharedBuffer 被排队到每个需要它的线程。

现在我想,我做了很多不必要的事情,我可以通过以下方案使用 boost::intrusive_ptr。事情有点 C'ish- 因为我使用可变大小的数组。为了简单起见,这里我用运算符 new() 更改了平板分配器。我想运行它,看看这个实现是否可以。

template <typename T>
inline int atomicIncrement (T* t)
{
    return __atomic_add_fetch(&t->_ref, 1, __ATOMIC_ACQUIRE);
}

template <typename T>
inline int atomicDecrement (T* t)
{
    return __atomic_sub_fetch(&t->_ref, 1, __ATOMIC_RELEASE);
}

class SharedBuffer {
public:

    friend int atomicIncrement<SharedBuffer>(SharedBuffer*);
    friend int atomicDecrement<SharedBuffer>(SharedBuffer*);

    SharedBuffer(uint16_t len) : _length(len) {}

    uint8_t *data ()
    {
        return &_data[0];
    }

    uint16_t length () const
    {
        return _length;
    }

private:

    int              _ref{0};
    const uint16_t   _length;
    uint8_t          _data[];
};

using SharedBufferPtr = boost::intrusive_ptr<SharedBuffer>;

SharedBufferPtr allocate (size_t size)
{
    // dummy implementation
    void *p = ::operator new (size + sizeof(SharedBuffer));

    // I am not explicitly constructing the array of uint8_t
    return new (p) SharedBuffer(size);
}

void deallocate (SharedBuffer* sbuf)
{
    sbuf->~SharedBuffer();

    // dummy implementation
    ::operator delete ((void *)sbuf);
}

void intrusive_ptr_add_ref(SharedBuffer* sbuf)
{
    atomicIncrement(sbuf);
}

void intrusive_ptr_release (SharedBuffer* sbuf)
{
    if (atomicDecrement(sbuf) == 0) {
        deallocate(sbuf);
    }
}

【问题讨论】:

    标签: boost c++14 shared-ptr


    【解决方案1】:

    我会使用更简单的实现(使用shared_ptr),除非您要避免特定问题(即首先配置文件)。

    旁注:您可以将boost::shared_pointer&lt;&gt; 与boost::make_shared&lt;T[]&gt;(N) 一起使用,c++20 正在[添加到标准库中。

    请注意,allocate_shared 已经像使用侵入式方法一样将控制块嵌入到相同的分配中。

    最后,我会使用std::atomic_int,这样您就有了一份明确的合同,不能(意外)被错误地使用。同时,它会消除剩余的复杂性。

    【讨论】:

      猜你喜欢
      • 2021-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多