使用 boost::intrusive_ptr 共享缓冲区



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

我最初的设计是使用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);
}
}

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

旁注 :您可以将boost::shared_pointer<>boost::make_shared<T[]>(N)一起使用,后者正在 [添加到 C++20 中的标准库中。

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

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

最新更新