Asio游戏网络中间件设计(三)数据结构
网络通信中有几种必要的数据结构,满足线程安全,高可复用等要求。
临时缓存
用于与内核缓冲区交互,由于在实现应用层协议时,常常要添加(或移除)包头等数据,为了减少memcpy的次数,我们将数据区以外的部分作为动态区,通过控制offset来保证数据的有效区域。
template <size_t SIZE>
class reusabel_buffer :public std::enable_shared_from_this<reusabel_buffer<SIZE>>
{
public:
inline unsigned char* buffer(size_t explicit_offset = 0)
{
return &(_buffer[offset + explicit_offset]);
}
inline std::shared_ptr<reusabel_buffer> reset()
{
offset = 0;
length = 0;
return shared_from_this();
}
inline size_t available_capacity()
{
return SIZE - offset;
}
inline unsigned char* origin_buffer(size_t explicit_offset = 0)
{
return &(_buffer[explicit_offset]);
}
size_t offset;
size_t length;
#pragma region string-style
inline unsigned char& operator[](size_t idx)
{
return _buffer[offset + idx];
}
inline void resize(size_t s)
{
length = s;
}
inline size_t size()
{
return length;
}
#pragma endregion
private:
unsigned char _buffer[SIZE];
};
无锁队列
无锁队列用于存放持久性的数据,比如提供给主线程读取的 storage,由于逻辑上读写分离,所以天然带有读写锁的属性,但是又比读写锁快很多,不过有一个坏处,内存是定长的,如果要进行 resize 会非常困难。
// consumer & producer are single
// lock-free queue
template<class T>
class RingBuffer
{
private:
T* _buffer;
size_t _readIndex;
size_t _writeIndex;
size_t _capacity;
public:
RingBuffer(size_t capacity);
~RingBuffer();
size_t count() const;
bool full(size_t add = 0) const;
bool empty(size_t del = 0) const;
#ifdef ALLOW_BLOCK_COPY
#pragma message("low-performance datablock copy implement, use tryReadWithoutCopy instead")
bool tryRead(size_t length, T* ret, bool withoutShift = false);
bool tryWrite(T* data, size_t length = 0);
#else
// @pram withoutShift
// unless u know why its lock-free for sure,
// do not use withoutShift = false
bool tryReadWithoutCopy(size_t length, T* toReadBlock1, size_t* toReadLength1, T* toReadBlock2, size_t* toReadLength2, bool withoutShift = true);
bool tryWriteWithoutCopy(size_t length, T* toWriteBlock1, size_t* toWriteLeng1, T* toWriteBlock2, size_t* toWriteLeng2, bool withoutShit = true);
#endif
bool tryReadShift(size_t length);
bool tryWriteShit(size_t length);
void clear();
};
template<class T>
inline RingBuffer<T>::RingBuffer(size_t capacity) :
_capacity(capacity),
_readIndex(0),
_writeIndex(0)
{
_buffer = (T*)malloc(sizeof(T) * capacity);
}
template<class T>
inline RingBuffer<T>::~RingBuffer()
{
free(_buffer);
}
template<class T>
inline size_t RingBuffer<T>::count() const
{
return (_writeIndex - _readIndex + _capacity) % _capacity;
}
template<class T>
inline bool RingBuffer<T>::full(size_t add) const
{
return (count() + add) >= _capacity;
}
template<class T>
inline bool RingBuffer<T>::empty(size_t del) const
{
return count() - del <= 0;
}
#ifdef ALLOW_BLOCK_COPY
template<class T>
inline bool RingBuffer<T>::tryRead(size_t length, T* ret, bool withoutShift)
{
if (empty())
return false;
for (int i = 0; i < length; ++i)
{
ret[i] = _buffer[(_readIndex + i) % _capacity];
}
if (LIKELY(!withoutShift))
{
_readIndex += length;
_readIndex %= _capacity;
}
return true;
}
template<class T>
inline bool RingBuffer<T>::tryWrite(T* data, size_t length)
{
if (full())
return false;
else
{
if (length + count() >= _capacity)
return false;
for (int i = 0; i < length; ++i)
{
_buffer[(_writeIndex + i) % _capacity] = data[i];
}
_writeIndex += length;
_writeIndex %= _capacity;
}
return true;
}
#else
template<class T>
inline bool RingBuffer<T>::tryReadWithoutCopy(size_t length, T* toReadBlock1, size_t* toReadLength1, T* toReadBlock2, size_t* toReadLength2, bool withoutShift)
{
if (UNLIKELY(empty(length)))
return false;
toReadBlock1 = &_buffer[_readIndex];
if (UNLIKELY(_readIndex + length > _capacity))
{
toReadLength1 = _capacity - _readIndex;
toReadBlock2 = _buffer;
toReadLength2 = length - _capacity + _readIndex;
}
else
{
toReadLength1 = length;
}
if (UNLIKELY(!withoutShift))
{
_readIndex += length;
_readIndex %= _capacity;
}
}
template<class T>
inline bool RingBuffer<T>::tryWriteWithoutCopy(size_t length, T* toWriteBlock1, size_t* toWriteLeng1, T* toWriteBlock2, size_t* toWriteLeng2, bool withoutShit)
{
if (UNLIKELY(full(length)))
return false;
else
{
toWriteBlock1 = &_buffer[_writeIndex];
if (UNLIKELY(_writeIndex + length > _capacity))
{
toWriteLeng1 = _capacity - _writeIndex;
toWriteBlock2 = _buffer;
toWriteLeng2 = length - _capacity + _writeIndex;
}
else
{
toWriteLeng1 = length;
}
if (UNLIKELY(!withoutShit))
{
_writeIndex += length;
_writeIndex %= _capacity;
}
}
return true;
}
#endif
template<class T>
inline bool RingBuffer<T>::tryReadShift(size_t length)
{
if (UNLIKELY(empty(length)))
return false;
_readIndex += length;
_readIndex %= _capacity;
return true;
}
template<class T>
inline bool RingBuffer<T>::tryWriteShit(size_t length)
{
if (UNLIKELY(full(length)))
return false;
_writeIndex += length;
_writeIndex %= _capacity;
return true;
}
template<class T>
inline void RingBuffer<T>::clear()
{
_readIndex = 0;
_writeIndex = 0;
}
其他数据结构
下面的结构也是常用的,但是实现比较多而且大同小异,此处不表。
1、线程安全的常用std容器封装,这些一般都是加锁的普通实现,也没有什么特殊的。
2、线程安全的各种工具类(比如saferandom等),这些直接用SafeSingleton进行封装,注意尽量少用static对象。