無鎖隊列
阿新 • • 發佈:2018-03-18
使用 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原理
無鎖隊列