高效读写消息队列SafeQueue

/******************高效消息队列 读写分离 自旋锁********************/

class SpinLock
{
public:
	SpinLock() { pthread_spin_init(&m_spinlock, 0); }
	~SpinLock() { pthread_spin_destroy(&m_spinlock); }
private:
	DISALLOW_COPY_AND_ASSIGN(SpinLock);
public:
	void lock() { pthread_spin_lock(&m_spinlock); }
	void unlock() { pthread_spin_unlock(&m_spinlock); }
private:
	pthread_spinlock_t m_spinlock;
};
/*	
	自旋锁就主要用在临界区持锁时间非常短且CPU资源不紧张的情况下 多核最好
	互斥锁用于临界区持锁时间比较长的操作
	互斥锁的起始开销要高于自旋锁,但是基本是一劳永逸,
	临界区持锁时间的大小并不会对互斥锁的开销造成影响,而自旋锁是死循环检测,
	加锁全程消耗cpu,起始开销虽然低于互斥锁,但是随着持锁时间,加锁的开销是线性增长
	sleep-waiting busy-waiting
*/
template 
class SafeQueue
{
public:
	SafeQueue()
		:m_pushqueue(NULL),m_popqueue(NULL)
		,m_pushqueueBegin(0),m_popqueueBegin(0)
		,m_pushqueueEnd(0),m_popqueueEnd(0)
		,m_pushqueueMaxSize(1),m_popqueueMaxSize(1)
	{
		m_pushqueue = new T[1];
		m_popqueue = new T[1];
	}
	~SafeQueue()
	{
		delete[] m_pushqueue;
		delete[] m_popqueue;
		m_pushqueue = NULL;
		m_popqueue = NULL;
	}
public:
	T pop()
	{
		m_poplock.lock();
		if (m_popqueueBegin >= m_popqueueEnd)
		{
			//无内容可读,交换 读写队列  这里是重点
			m_pushlock.lock();
			T* oldpush = m_pushqueue;
			int32_t maxSize = m_pushqueueMaxSize;
			int32_t pushend = m_pushqueueEnd;

			m_pushqueue = m_popqueue;
			m_pushqueueBegin = 0;			
			m_pushqueueEnd = 0;
			m_pushqueueMaxSize = m_popqueueMaxSize;			
			m_pushlock.unlock();
			//写队列,切换到之前的pop队列, 从头开始写,因为前面的都读过了,所以覆盖
			//最大的长度就是pop的最大长度,
			//当然就是 上次这里还是写队列 的时候的 maxsize


			//读队列 切到写队列头 从头开始读 最多读到pushend 读的最大就是写队列的最大 后面很多还没写也有可能
			m_popqueue = oldpush;
			m_popqueueBegin = 0;
			m_popqueueEnd = pushend;
			m_popqueueMaxSize = maxSize;
			
		}
		//下面开始读
		if (m_popqueueBegin < m_popqueueEnd)
		{
			//正常
			T ret = m_popqueue[m_popqueueBegin++];
			m_poplock.unlock();
			return ret;
		}
		else
		{
			//没东西
			m_poplock.unlock();
			return T(0);
		}
	}
	void push(T val)
	{
		T* oldpos = NULL;
		m_pushlock.lock();
		if (m_pushqueueEnd >= m_pushqueueMaxSize)
		{
			oldpos = m_pushqueue;
			m_pushqueue = new T[m_pushqueueMaxSize * 2];
			memcpy(m_pushqueue, oldpos, sizeof(T)*m_pushqueueEnd);
			m_pushqueueMaxSize *= 2;
		}
		m_pushqueue[m_pushqueueEnd++] = val;
		m_pushlock.unlock();

		SAFE_DELETE(oldpos);//存储内容拷贝过去 之前的内容需要清掉
	}
private:
	DISALLOW_COPY_AND_ASSIGN(SafeQueue);
private:
	//两个队列,一个用于push 一个用于pop 节省了锁的开销 加快了速度
	//当读完的时候,换指针,
	//读指针指向push的队列,从头开始读, 
	//写指针指向pop的队列 之前的已经读过所以覆盖 继续从头开始写
	T* m_pushqueue;
	T* m_popqueue;
	//下面的指针 就是记录读写位置的,方便读写交换队列用
	int32_t m_pushqueueEnd;
	int32_t m_popqueueEnd;
	
	int32_t m_pushqueueBegin;
	int32_t m_popqueueBegin;

	int32_t m_pushqueueMaxSize;
	int32_t m_popqueueMaxSize;

	//两个队列的自旋锁 短时间高速适用
	SpinLock m_pushlock;
	SpinLock m_poplock;
};

你可能感兴趣的:(记录)