C++ 無鎖隊列實現


上源碼

  1 #ifndef __GLOBAL_LOCK_FREE_QUEUE_H__
  2 #define __GLOBAL_LOCK_FREE_QUEUE_H__
  3 
  4 #include <atomic>
  5 #include <list>
  6 
  7 #ifdef _WINDOWS
  8 #include <windows.h>
  9 //PVOID __cdecl InterlockedCompareExchangePointer(
 10 //    _Inout_ PVOID volatile *Destination,
 11 //    _In_    PVOID          Exchange,
 12 //    _In_    PVOID          Comparand
 13 //    );
 14 //
 15 //Parameters
 16 //    Destination [in, out]
 17 //A pointer to a pointer to the destination value.
 18 //    Exchange [in]
 19 //The exchange value.
 20 //    Comparand [in]
 21 //The value to compare to Destination.
 22 //    Return value
 23 //    The function returns the initial value of the Destination parameter.
 24 //    Remarks
 25 //    The function compares the Destination value with the Comparand value. If the Destination value is equal to the Comparand value, the Exchange value is stored in the address specified by Destination. Otherwise, no operation is performed.
 26 //    On a 64-bit system, the parameters are 64 bits and must be aligned on 64-bit boundaries; otherwise, the function will behave unpredictably. On a 32-bit system, the parameters are 32 bits and must be aligned on 32-bit boundaries.
 27 //    The interlocked functions provide a simple mechanism for synchronizing access to a variable that is shared by multiple threads. This function is atomic with respect to calls to other interlocked functions.
 28 #define __sync_bool_compare_and_swap(a,b,c) (InterlockedCompareExchangePointer((void*volatile*)a,c,b), (*a)==(c))
 29 #endif
 30 
 31 namespace DataCache
 32 {
 33 
 34     template <typename T>
 35     class LinkList
 36     {
 37     public:
 38         T data;
 39         LinkList<T> *next;
 40     };// class LinkeList<T>
 41 
 42     template <typename T>
 43     class LockFreeQueue
 44     {
 45     public:
 46         LockFreeQueue();
 47 
 48         void push_back(T t);
 49 
 50         T pop_front(void);
 51 
 52         bool isEmpty(void);
 53 
 54         int GetLength();
 55 
 56     private:
 57         LinkList<T> *head_;
 58         LinkList<T> *tail_;
 59         std::_Atomic_integral_t elementNumbers_;
 60     }; // class LockFreeQueue
 61 
 62     template <typename T>
 63     LockFreeQueue<T>::LockFreeQueue()
 64         :head_(NULL),
 65         tail_(new LinkList<T>),
 66         elementNumbers_(0)
 67     {
 68         head_ = tail_;
 69         tail_->next = NULL;
 70     }
 71 
 72     template <typename T>
 73     void LockFreeQueue<T>::push_back(T t)
 74     {
 75         auto newVal = new LinkList<T>;
 76         newVal->data = t;
 77         newVal->next = NULL;
 78 
 79         LinkList<T> *p;
 80         do
 81         {
 82             p = tail_;
 83         } while (!__sync_bool_compare_and_swap(&tail_->next, NULL, newVal));
 84 
 85         //move tail_
 86         __sync_bool_compare_and_swap(&tail_, tail_, newVal);
 87         elementNumbers_++;
 88     }
 89 
 90     template <typename T>
 91     T LockFreeQueue<T>::pop_front()
 92     {
 93         LinkList<T> *p;
 94 
 95         do
 96         {
 97             //record the first node.
 98             p = head_->next;
 99             if (!p)
100             {
101                 return 0;
102             }
103         } while (!__sync_bool_compare_and_swap(&head_->next, p, p->next));
104 
105         if (elementNumbers_ > 0) elementNumbers_--;
106         if (elementNumbers_ == 0)
107         {
108             // if the queue is empty then the tail to header.
109             do
110             {
111 
112             } while (!__sync_bool_compare_and_swap(&tail_, p, head_));
113         }
114 
115         return p->data;
116     }
117 
118     template <typename T>
119     bool LockFreeQueue<T>::isEmpty()
120     {
121         if (elementNumbers_ == 0)
122         {
123             return true;
124         }
125         else
126         {
127             return false;
128         }
129     }
130 
131     template <typename T>
132     int LockFreeQueue<T>::GetLength()
133     {
134         return elementNumbers_;
135     }
136 
137 }// namespace DataCache
138 
139 #endif
View Code

 

 

源碼解析:

重點在函數InterlockedCompareExchangePointer(a,c,b),其作用為如果a==b,則修改a的值為c,這是微軟提供的一個原子操作的API,這里面通過定義這個宏,來進行queue入隊列和出隊列時的指針修改,達到無鎖操作的目的,可以大幅提高隊列的操作性能,避免過程中進行加鎖操作。在GCC中已經原生支持了__sync_bool_compare_and_swap,所以這個宏的作用是針對MSVC版本提供的類似函數。

 

測試代碼:

 1 #include"lockFreeQueue.h"
 2 
 3 DataCache::LockFreeQueue<int> queue;
 4 
 5 void func1(void)
 6 {
 7     for(int i = 0; i <100000 ; i++)
 8     {
 9         queue.push_back(i);
10     }
11 }
12 
13 void func2(void)
14 {
15     for(int i = 100000; i < 200000; i++)
16     {
17         queue.push_back(i);
18     }
19 }
20 
21 void func5(void)
22 {
23     for(int i = 200000; i < 300000; i++)
24     {
25         queue.push_back(i);
26     }
27 }
28 
29 void func4(void)
30 {
31     for(int i = 400000; i < 500000; i++)
32     {
33         queue.push_back(i);
34     }
35 }
36 
37 void func3(void)
38 {
39     while (!queue.isEmpty())
40     {
41         std::cout<<queue.pop_front()<<" ";
42     }
43 
44     std::cout<<std::endl;
45 }
46 
47 int main(int argc,char **argv)
48 {
49     std::thread t1(func1);
50     std::thread t2(func2);
51     std::thread t5(func5);
52     std::thread t6(func4);
53 
54     t1.join();
55     t2.join();
56     t5.join();
57     t6.join();
58 
59     int length=queue.GetLength();
60     assert(length==500000);
61     return 0;
62 }

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM