認識無鎖佇列
無鎖佇列是 lock-free 中最基本的資料結構,一般應用在需要一款高效能佇列的場景下。 |
對於多執行緒使用者來說,無鎖佇列的入隊和出隊操作是執行緒安全的,不用再加鎖控制。
什麼是無鎖佇列佇列每個開發者都知道,那麼什麼又是無鎖佇列呢?字面理解起來就是一個無鎖狀態的佇列,多個執行緒(消費者)同時操作資料的時候不需要加鎖,因為加/解鎖都是一個很消耗資源的動作。
資料結構我們先看一下無鎖佇列的底層實現資料結構。
無鎖佇列底層的資料結構實現方式主要有兩種:陣列 和 連結。
陣列在首次初始化時,需要申請一塊連線的大的記憶體。讀寫資料直接從資料的指定位置操作即可,時間複雜度為O(1)。
缺點:陣列長度有限,一旦陣列索引位置寫滿,則無法繼續寫入,即佇列有上限。
不用像陣列一樣,剛開始就申請一塊連線的大的記憶體空間。只有在每次寫時資料的時候,申請這個資料節點大小的記憶體即可,這樣就可以實現無限的寫入,沒有長度限制問題。
缺點:每次寫資料都要申請記憶體,在寫的場景,最差的情況是多少個數據就申請多少次記憶體,而每次申請都是一個消耗資源的動作。
可以看到無鎖底層的實現的不同各有優勢。多資料情況下,我們都採用連結串列來實現無鎖佇列,主要原因就是寫入可以沒有長度的限制。相比每次申請都要費時來說,滿足前面的條件是我們最基本的要求。當然主要還是真正的使用場景。
CASCAS 是 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
現在幾乎所有的CPU指令都支援CAS的原子操作,X86下對應的是 CMPXCHG 彙編指令。有了這個操作,我們就可以用其來實現各種無鎖的資料結構。
使用場景無鎖佇列也屬於佇列的一種,所以大部分佇列的使用場景都可以使用它來代替其它有鎖佇列,無鎖佇列通過不加鎖的方式提高佇列效能。
本文地址:https://www.linuxprobe.com/understanding-lockless-queue.html