Asio游戏网络中间件设计(三)数据结构

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对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值