我有一个用例,其中一个线程将消息读入一个大缓冲区,并将处理分配给一堆线程。之后,缓冲区由多个线程共享。它是只读的,当最后一个线程完成时,必须释放缓冲区。缓冲区是从无锁的slab分配器中分配的。
我最初的设计是使用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()更改了slab分配器。我想运行它以查看此实现是否正常。
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);
}
}
答案 0 :(得分:0)
我会使用更简单的实现(使用shared_ptr
),除非您避免特定问题(即首先配置文件)。
附注 :您可以将
boost::shared_pointer<>
与boost::make_shared<T[]>(N)
一起使用,[{1}}将其添加到标准库中
请注意allocate_shared
已经将控制块嵌入到相同的分配中,就像使用侵入式方法一样。
最后,我使用std::atomic_int
所以你有一个明确的合同,不能(意外地)使用错误。与此同时,它将消除剩余的复杂性。