【发布时间】: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