可遍历的无锁队列
来源:互联网 发布:淘宝商家手机怎么注册 编辑:程序博客网 时间:2024/04/30 15:02
是基于希望在接到网络消息后,能不受线程限制,在任意线程进行访问消息,减小网络线程压力而实现的一个辅助工具。
比简单的无锁队列稍显复杂,仅在小规模情况下使用过,尚不确定是否还有什么隐患。
#ifdef _MSC_VER#pragma once#endif#ifndef __TraverseQueue_H__#define __TraverseQueue_H__#include "Macro.h"// forward declaration//////////////////////////////////////////////////////////////////////////// @file TraverseQueue.h (遍历队列)// @brief 可遍历的多线程无锁队列( 要求 同一队列所有的压入操作在同一个线程中,所有压出操作在同一个线程中)// 从队首压出元素,从队尾压入元素。 保证压入,压出 和 复数以上的循环读取 之间线程安全// 局限性: 需保证被取出的对象要用来获得下一对象或主动进行释放,否则可能导致元素阻塞,大量的无用的元素得不到自动释放(因为释放是按队列依次释放的)// 建议在一个封闭的小环境内进行使用// @author 张逸云// @date 2011.09.27//////////////////////////////////////////////////////////////////////////template<class T>class CTraverseQueue{public: struct S_TQ_Element // 链 { friend class CTraverseQueue; private: S_TQ_Element( void ) { Init(); } void Init( void ) { _pNext = NULL; _nReadCount = 0; _bErase = false; } public: inline const T& GetValue( void ) const { return _value; } private: T _value; S_TQ_Element* _pNext; unsigned int _nReadCount; // 使用者计数,没有使用者了才会被删除 bool _bErase; // 删除标记,用于队首指针移动。且被标记为删除的元素不会再被读出 };public: CTraverseQueue( void ); ~CTraverseQueue( void ); /** @brief 向队尾压入一个元素 */ void PushBack( const T& Val ); /** @brief 清理待删除队列中所有无效元素(在m_pReadFirstF之前,且使用者计数为0), 不会直接删除而是放入空闲列表中,待重用 */ void PopErase_Front( void ); /** @brief 获得队首的第一个可读元素,以进行遍历. 并自动增加该元素的使用者计数, 如果没有可读元素将返回 NULL * @warning 请确保被取出的元素指针被用来或取下一元素或被释放了!!!!!!!否则可能造成阻塞,大量无用元素得不到释放 */ S_TQ_Element* GetFirstRead( void ) { if ( m_pReadFirstF != m_pReadLast ) // 当第一个可读元素的前一位与队位指针重合时,说明不存在可读元素 { m_pReadFirstF->_pNext->_nReadCount++; return m_pReadFirstF->_pNext; } return NULL; } /** * @brief 获得下一个可读读元素。 * @param ppElm, 当前元素指针。如果下一元素可读,将会被赋值为下一元素,否则将会被赋值为 NULL * 并会自动减少当前元素的使用计数。增加下一元素的计数(如果可读) * @param bErase, 标记当前元素为删除,不可再被读取(对于类的使用者而言,相当于被删除),且自动调整m_pReadFirstF指针。 * @return bool, 如果拥有下一可读元素, 将返回 true, 否则返回 false(没有可读得下一位或传入值无效). * @warning 请确保被取出的元素指针被用来或取下一元素或被释放了!!!!!!!否则可能造成阻塞,大量无用元素得不到释放 */ bool GetNextRead( __in __out S_TQ_Element** ppElm, bool bErase = true ); /** @brief 主动释放当前元素。 并将其本身置为 NULL 进行返回,以避免对无效值的使用。 会自动减少该元素的使用计数,自动调整m_pReadFirstF指针。*/ void ReleaseElement( __in __out S_TQ_Element** ppElm );private: S_TQ_Element* m_pReadFirstF; // 第一个可读元素的前一位 S_TQ_Element* m_pReadLast; // 最后一个可读元素 S_TQ_Element* m_pFirstDel; // 待删除队列的队首。 待删除队列中的元素不可被或取,当_nReadCount为0时将进行实际删除(元素无效,被压入重用池) // 待删除队列的队尾与可读队列相连 S_TQ_Element* m_pIdleFirst; // 存储空闲元素的列表,用于重用(以队列的方式来进行使用) S_TQ_Element* m_pIdleLast; // 存储空闲元素的列表的最后一个元素};//////////////////////////////////////////////////////////////////////////template<class T>CTraverseQueue<T>::CTraverseQueue( void ): m_pReadFirstF( new S_TQ_Element() ), m_pReadLast( NULL ), m_pFirstDel( new S_TQ_Element() ), m_pIdleFirst( new S_TQ_Element() ), m_pIdleLast( NULL ){ m_pReadFirstF->_bErase = true; // 设置队尾 m_pReadLast = m_pReadFirstF; // 可读队列 m_pFirstDel->_pNext = m_pReadFirstF; // 待删除队列 m_pIdleLast = m_pIdleFirst; // 重用队列}template<class T>CTraverseQueue<T>::~CTraverseQueue( void ){ // 依次释放所有元素的空间 S_TQ_Element* pDelTemp = NULL; // 删除重用队列 while ( m_pIdleFirst ) { pDelTemp = m_pIdleFirst; m_pIdleFirst = m_pIdleFirst->_pNext; SAFE_DELETE( pDelTemp ); } // 删除待删除和可读队列 while ( m_pFirstDel ) { pDelTemp = m_pFirstDel; m_pFirstDel = m_pFirstDel->_pNext; SAFE_DELETE( pDelTemp ); }}template<class T>void CTraverseQueue<T>::PushBack( const T& Val ){ S_TQ_Element* pPushElm = NULL; if ( m_pIdleFirst != m_pIdleLast ) { pPushElm = m_pIdleFirst; m_pIdleFirst = m_pIdleFirst->_pNext; pPushElm->Init(); } else { pPushElm = new S_TQ_Element(); } pPushElm->_value = Val; m_pReadLast->_pNext = pPushElm; m_pReadLast = pPushElm; // 原子操作,不用担心多线程问题}template<class T>void CTraverseQueue<T>::PopErase_Front( void ){ // 清理待删除队列中 无人再使用的元素 // 从队头开始清理,同步移动队头指针 S_TQ_Element* pDelTemp = NULL; while ( m_pFirstDel->_pNext != m_pReadFirstF && m_pFirstDel->_nReadCount == 0 ) { pDelTemp = m_pFirstDel; m_pFirstDel = m_pFirstDel->_pNext; // 清理到空闲容器中 pDelTemp->Init(); m_pIdleLast->_pNext = pDelTemp; m_pIdleLast = m_pIdleLast->_pNext; } // 如果存在仍被使用的元素,需要跳过继续删除(此后将不再同步移动m_pFirstDel指针) if ( m_pFirstDel->_pNext != m_pReadFirstF ) { S_TQ_Element* pLastTemp = m_pFirstDel; pDelTemp = pLastTemp->_pNext; while ( pDelTemp->_pNext != m_pReadFirstF ) { if ( pDelTemp->_nReadCount == 0 ) // 无人再进行使用,清理 { pLastTemp->_pNext = pDelTemp->_pNext; // 清理到空闲容器中 pDelTemp->Init(); m_pIdleLast->_pNext = pDelTemp; m_pIdleLast = m_pIdleLast->_pNext; pDelTemp = pLastTemp->_pNext; } else // 跳过,累加移位 { pLastTemp = pDelTemp; pDelTemp = pDelTemp->_pNext; } } }}template<class T>bool CTraverseQueue<T>::GetNextRead( __in __out S_TQ_Element** ppElm, bool bErase = true ){ S_TQ_Element* pCurElm = *ppElm; *ppElm = NULL; if ( pCurElm ) { // 移动到下一可读位 S_TQ_Element* pMovElm = pCurElm; while ( pMovElm && pMovElm != m_pReadLast ) { pMovElm = pMovElm->_pNext; if ( pMovElm->_bErase == false ) // 找到了一个有效的可读位 { pMovElm->_nReadCount++; *ppElm = pMovElm; break; } } // 释放当前位 if ( bErase ) { ReleaseElement( &pCurElm ); // 这里面也同样进行递减操作 } else { pCurElm->_nReadCount--; } } return *ppElm == NULL ? false : true;}template<class T>void CTraverseQueue<T>::ReleaseElement( __in __out S_TQ_Element** ppElm ){ S_TQ_Element* pTemp = *ppElm; if ( pTemp ) { *ppElm = NULL; pTemp->_bErase = true; pTemp->_nReadCount--; // 调整m_pReadFirstF指针 while ( m_pReadFirstF != m_pReadLast // 不是结束元素,下一位可用 && m_pReadFirstF->_bErase == true ) { m_pReadFirstF = m_pReadFirstF->_pNext; } }}#endif // __TraverseQueue_H__
- 可遍历的无锁队列
- 一个可无限伸缩且无ABA问题的无锁队列
- 无向图的深度广度遍历 递归 队列
- 无锁队列的实现
- 无锁队列的实现
- 无锁队列的实现
- 无锁队列的实现
- 无锁队列的实现
- 无锁队列的实现
- 无锁队列的实现
- 无锁队列的实现
- 无锁队列的实现
- 无锁队列的实现
- 无锁队列的实现
- 无锁队列的实现
- 无锁队列的实现
- 多线程的无锁队列
- 无锁队列的实现
- MYSQL/PHP/XAJAX/PHPMYADMIN字符集问题梳理
- PHP连接 SQL Server2005 失败,Fatal error: Call to undefined function mssql_connect() in
- Silverlight中TreeView,TreeViewItem,HierarchalDataTemplate的详细用法
- 让IE9.0一下的IE浏览器全面支持html5和css3.0
- ACCESS连接字符串
- 可遍历的无锁队列
- C语言堆栈入门
- 安装sharepoint 2010 必备组件
- 动态选择屏幕
- mysql root密码重置
- equals和hashcode的重写规则
- 安装boost和CGAL
- cenOs vsftp配置
- 'telnet' 不是内部或外部命令,也不是可运行的程序或批处理文件