lock free 实现多线程安全链表

来源:互联网 发布:nginx安装lua模块 编辑:程序博客网 时间:2024/05/23 17:37

   lock free  实现的多线程链表通常无法避免 ABA问题。ABA 问题的实质就是我们刚释放的内存可能会被马上又分配出来,被其他线程又放入到链表里了,导致Interlock函数判断链表节点没有改变(实际上节点已经被删除过一次了,链表发生了改变),而导致错误。

   那么解决方法最有3中:  一种不使用 interlock函数,一种就是在内存中增加计数标志,一种就是不释放内存。

   不使用 Interlock 肯定不现实;),通常认为非对称内存服务器下interlock 的效率比较高,重要的可能是interlock函数不会导致程序停顿,就是每一步都有进展 ,这样就不可能发生死锁。

   增加计数标志,原本在interlock中我们比较内存地址, 使用了计数标志后,新分配的节点比较对这个计数器+1。这样比较内存地址相同的节点时,我们再同时多检查一次计数器。这样就不会把新添加的节点误会作已有的旧节点了。不过因为要同时判断地址和计数标志,这个方案实现较复杂。

  

这里我采用了不释放内存这个方案。就是在链表的节点释放时, 将节点放到内存队列尾部里,在申请节点内存时,从内存队列头部取出被释放的内存。 这里有一个问题:

  如果内存队列中保存的释放节点数太少的话,就回出现刚释放的内存立刻又被分配出去了。所以, 我们再分配内存的时候, 必须保证内存队列内节点有一定的长度,这样被分配出去的内存节点可能是很久前被释放的内存。  那么这个长度多少合适呢?其实应该节点数超过线程数量1倍应该就是安全的了。因为只需要保证竞争的线程间不会使用同样的节点即可。

   实现代码如下:

template<class T>
class LinkQueue{
 
public:
    typedef  bool (*pfnFindCompare)(T a,T b);
    typedef NodeLinkGC<Node<T>> LinkGC;

private:
    Node<T>* m_pHead;
    Node<T>* m_pTail;
    Node<T> *m_pEmpty;
    LinkGC* m_pGC;
public:
    LinkQueue()
        :m_pEmpty(NULL),
        m_pHead (NULL),
        m_pTail(NULL),
        m_pGC(NULL)
    {
        m_pEmpty = new Node<T>;
        m_pHead = m_pEmpty;
        m_pTail = m_pEmpty;
        m_pGC  = new LinkGC(1024);

    }
 
    virtual ~LinkQueue()
    {
        if(m_pEmpty)
            delete m_pEmpty;
    }



    int Push(T ele)
    {

        Node<T> * pAdd =m_pGC->Allocate(); //通过内存GC来分配


        pAdd->pNext = NULL;
        pAdd->ele = ele ;

    
        while(InterlockedCompareExchangePointer((volatile PVOID *)&m_pTail->pNext,pAdd,NULL)!=NULL){};
    
        return 0;
    }

    bool Find(T ele)
    {
                
        if(m_pHead == m_pTail)
            return FALSE;
        Node * ph = m_pHead->pNext;
        Node * pe= m_pTail;
        while(ph!=pe)
        {
            if(ph->ele == ele)
                return true;
            else
                ph=ph->pNext;
        }

        return false;

    }
    bool FindCompare(T& ele,pfnFindCompare pfn)
    {
        if(m_pHead == m_pTail)
            return FALSE;
        Node<T> * ph = m_pHead->pNext;
        Node<T> * pe= m_pTail;
        while(ph!=pe)
        {
            if(pfn(ph->ele,ele))
            {
                ele = ph->ele;
                return true;
            }else
                ph=ph->pNext;
        }
        return false;
    }

    int Delete(T  ele)
    {
        NumberTaking a;


        if(m_pHead==m_pTail)
            return -1;
        Node<T> * ph = m_pHead->pNext;
        Node<T> * pe = m_pTail;
        Node<T>* pPre =m_pEmpty;
        LinkGC* pGc = LinkGC::CreateInstance();
        while(ph!=pe)
        {
            if(ph->ele == ele)
            {
                if(pPre==m_pEmpty)
                {
                    Node<T>* pt= PopNode();
                    pGc->Release(pt);
                    return 0;
                }
                else
                {
                    pPre->pNext = ph->pNext;
                    delete ph;
                    pGc->Release(ph);
                    return 0;
                }
            }
            else
            {
                pPre = ph;
                ph=ph->pNext ;
            }
        }

        return -1;

    }
    int Pop(T & ele)
    {
        Node<T>* pNode = PopNode();

        if(!pNode)
            return -1;

        ele = pNode->ele ;

        
        m_pGC->Release(pNode);

        return 0;
    }

private:

    Node<T>* PopNode()
    {


        Node<T>* pOld=NULL  ;
        Node<T> *pNext  =NULL;
        do{
            
            pOld=m_pHead->pNext;
            Node<T> *pNext = pOld->pNext;
        }
        while(InterlockedCompareExchangePointer((volatile PVOID*)&m_pHead->pNext,pNext,pOld)!=pOld);

        return pOld;
    }


};

内存收集器:

template<class T>

class NodeLinkGC
{
    int m_iLimitCount ;

 
    RingQueue<T*> *m_pQueue;
    NodeLinkGC()
    {}

public:

    /*

    
    */
    explicit NodeLinkGC(const unsigned int sz,const int limitCount=128)
    {
      m_pQueue = new RingQueue<T*>(sz);

      if(m_pQueue ==NULL)
      {
          throw "NodeLinkGc construct error";
      }
      m_iLimitCount = limitCount;
    }

 
    /*
 
      这个可能还用不上
    */
    static NodeLinkGC<T> * CreateInstance(const int sz,const int limitSize )
    {
        static volatile NodeLinkGC<T> * g_pGC=NULL;

        NodeLinkGC<T> *  pGC = NULL;
        
        if(g_pGC ==NULL)
        {
            try{
            pGC = new NodeLinkGC<T>(sz,limitSize);
        
            }
            catch(char* pError)
            {
                OutputDebugStringA(pError);
                return NULL;
            }
            if(pGC ==NULL)
            {
                OutputDebugStringA("NodeLinkGc createInstance failed");
                return NULL;
            }

            if(::InterlockedCompareExchangePointer((volatile PVOID*)&g_pGC,pGC,NULL) !=NULL)
            {
                delete pGC ;//如果交换失败,证明有其他线程将pGC 已经分配了,所以我们要释放自己的内存
            }
        }
        
        return (NodeLinkGC<T>*)g_pGC;


    }

    T* Allocate()
    {
        if(m_pQueue)
        {
            //m_iLimitCount的判断,可以确保被释放到GC 的内存被保留足够的时间 ,以避免ABA 问题
            if((m_pQueue->IsEmpty())|| m_pQueue->GetCount()>m_iLimitCount)
                return new T;
            else
            {
                T* p=NULL;
                
                int ret = m_pQueue->Pop(p);

                if(ret==0)
                    return p ;
                else       
                { //发生这种情况通常是队列里node数量太少,如果大量线程一直申请的话,
                    // 容易发生有部分线程一直没拿到内存的情况,所以这里不对m_pQueue做严格的分配
                    //即使m_pQuque里有几个node ,为保证分配成功,也还是会new
                    return (new T);
                }

            }
        }
        return NULL;
    }

    int Release(T * p)
    {
        if(p)
        {
            if(0==m_pQueue->Push(p))
                return 0;
        }
        return -1;
    }
};


  


原创粉丝点击