1. 程式人生 > >無鎖隊列

無鎖隊列

使用 log 偽命題 就會 時間 brush 設置 sta 不用

1 偽命題

這本身是個偽命題。

多線程之間使用隊列是一定需要做到同步的。也就是說一定是需要同步手段的,一定要在一個線程讀寫的時候,阻塞另一個線程。既然不然用鎖,那就是用原子變量吧。

2 CAS

3 實現

隊列,這裏使用鏈表來實現

struct ListNode
{
 ListNode *next;
 int val;     
 ListNode(int x):val(x),ListNode(nullptr){}
};

class UnlockQueue
{
public:
   void push(int x);
   int pop(); 
private:
  ListNode *push_start;//
  ListNode *pop_start;
};

  

然後,考慮push,在push的時候,要在push_start的next上加節點,此時push_start->next是等於nullptr的。

void UnlockQueue::push(int x)
{
 ListNode *n=new ListNode(x);
 ListNode *tmp=push_start;
  
 while(cas(push_start->next,nullptr,n)!=true)
 {
   tmp=push_start;
 }
  push_start=n;
}

  

解釋一下,創建要添加的節點以後,就用cas輪序push_start->next,當等於nullptr的時候,就將next設置為n。

因為是個原子操作,因此同一時刻,就只有這個線程在設置next。同時,當別的線程設置了next以後,那麽next就不在指向nullptr,那麽就會一直循環。

當設置完以後,就將push_start設置為n,此時其他線程,tmp指向改變,真正的指向尾部,因此其他線程就可在cas通過了。

因此本質上還是鎖。但是這裏使用了輪詢。

原因在於,這個設置過程時間很短暫,所以沒有必要使用鎖。

這裏,類的兩個字段,可以不用加上volatile關鍵字修飾,因為別的線程會改變自己正在使用的數據。因為在cas的實現上,這兩個數據成員所處的內存一直是由某個cpu獨享的。因此不會有其他核心去改變這個數據。

看到這裏估計也就明白了,這其實是一個自己實現的自旋鎖,嗯,自旋鎖,使用輪詢的方式,不讓進程阻塞。

對應的pop操作,同理,也是這樣的思想。

  

4 CAS原理

無鎖隊列