認識無鎖佇列
對於多執行緒使用者來說,無鎖佇列的入隊和出隊操作是執行緒安全的,不用再加鎖控制。
佇列每個開發者都知道,那麼什麼又是無鎖佇列呢?字面理解起來就是一個無鎖狀態的佇列,多個執行緒(消費者)同時運算元據的時候不需要加鎖,因為加/解鎖都是一個很消耗資源的動作。
我們先看一下無鎖佇列的底層實現資料結構。
無鎖佇列底層的資料結構實現方式主要有兩種:陣列 和 連結。
在首次初始化時,需要申請一塊連線的大的記憶體。讀寫資料直接從資料的指定位置操作即可,時間複雜度為O(1)。
缺點:陣列長度有限,一旦陣列索引位置寫滿,則無法繼續寫入,即佇列有上限。
不用像陣列一樣,剛開始就申請一塊連線的大的記憶體空間。只有在每次寫時資料的時候,申請這個資料節點大小的記憶體即可,這樣就可以實現無限的寫入,沒有長度限制問題。
缺點:每次寫資料都要申請記憶體,在寫的場景,最差的情況是多少個資料就申請多少次記憶體,而每次申請都是一個消耗資源的動作。
可以看到無鎖底層的實現的不同各有優勢。多資料情況下,我們都採用連結串列來實現無鎖佇列,主要原因就是寫入可以沒有長度的限制。相比每次申請都要費時來說,滿足前面的條件是我們最基本的要求。當然主要還是真正的使用場景。
CAS 是 Compare And Swap 的簡稱, 屬於 樂觀鎖,這是一個併發同步原語. 虛擬碼如下:
bool compare_and_swap(int *reg, int oldval, int newval) { int reg_val = *reg; if(reg_val == oldval) { *reg = newval; return true; } return false; }
CAS操作有三個引數,分別表示 記憶體值V、舊的預期值A 和 修改後的更新值B。
判斷變數記憶體某個位置的值是否為預期值,如果是則更改為新的值,並返回true,這個過程是原子性操作。如果修改失敗,可能需要重試再次執行CAS操作,直到修改成功,一般稱此過程為自旋。可以看到每次呼叫 CAS 前需要先讀取舊值 oldval。
現在幾乎所有的CPU指令都支援CAS的原子操作,X86下對應的是 CMPXCHG 彙編指令。有了這個操作,我們就可以用其來實現各種無鎖的資料結構。
無鎖佇列也屬於佇列的一種,所以大部分佇列的使用場景都可以使用它來代替其它有鎖佇列,無鎖佇列透過不加鎖的方式提高佇列效能。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69901823/viewspace-2777948/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 無鎖佇列佇列
- 無鎖資料結構:佇列資料結構佇列
- 實現無鎖的棧與佇列(4)佇列
- 實現無鎖的棧與佇列(3)佇列
- 實現無鎖的棧與佇列(1)佇列
- 實現無鎖的棧與佇列(2)佇列
- 高效能無鎖佇列 Disruptor 初體驗佇列
- RabbitMQ訊息佇列系列教程(一)認識RabbitMQMQ佇列
- 效能優化-使用雙buffer實現無鎖佇列優化佇列
- 實現無鎖的棧與佇列(5):Hazard Pointer佇列
- 佇列、資源與鎖佇列
- 你應該知道的高效能無鎖佇列Disruptor佇列
- 一個高效能無鎖非阻塞連結串列佇列佇列
- 訊息佇列概念與認知佇列
- 【資料結構】佇列的基礎知識(無程式碼)資料結構佇列
- 抽象佇列同步器(獨佔鎖)抽象佇列
- 知識分享--訊息佇列佇列
- Oracle佇列鎖enq:TS,Temporary Segment (also TableSpace)Oracle佇列ENQ
- 基於佇列的鎖:mcs lock簡介佇列
- 佇列、阻塞佇列佇列
- 認識RAID磁碟陣列AI陣列
- 佇列-單端佇列佇列
- 深入學習Lock鎖(1)——佇列同步器佇列
- 001@多用派發佇列,少用同步鎖佇列
- Redis 分散式鎖與任務佇列實戰Redis分散式佇列
- Go和C語言的32 位的無鎖、併發、通用佇列的原始碼GoC語言佇列原始碼
- 模型的 save() 方法無法使用佇列?模型佇列
- 佇列 和 迴圈佇列佇列
- 【佇列】【懶排序】佇列Q佇列排序
- 從JDK角度認識列舉enumJDK
- 認識 Go 語言中的陣列Go陣列
- js二維陣列新認識JS陣列
- 陣列模擬佇列 以及佇列的複用(環形佇列)陣列佇列
- 分散式訊息佇列知識圖譜分散式佇列
- 佇列 手算到機算 入門 佇列 迴圈佇列佇列
- 棧、堆、佇列深入理解,面試無憂佇列面試
- 什麼?無限緩衝的佇列(一)?佇列
- 什麼?無限緩衝的佇列(二)?佇列