可遍历的无锁队列

是基于希望在接到网络消息后,能不受线程限制,在任意线程进行访问消息,减小网络线程压力而实现的一个辅助工具。

比简单的无锁队列稍显复杂,仅在小规模情况下使用过,尚不确定是否还有什么隐患。

 

#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__


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值