無鎖隊列是 lock-free 中最基本的數據結構,一般應用在需要一款高性能隊列的場景下。 |
對於多線程用戶來說,無鎖隊列的入隊和出隊操作是線程安全的,不用再加鎖控制。
隊列每個開發者都知道,那么什么又是無鎖隊列呢?字面理解起來就是一個無鎖狀態的隊列,多個線程(消費者)同時操作數據的時候不需要加鎖,因為加/解鎖都是一個很消耗資源的動作。
我們先看一下無鎖隊列的底層實現數據結構。
無鎖隊列底層的數據結構實現方式主要有兩種:數組 和 鏈接。
在首次初始化時,需要申請一塊連接的大的內存。讀寫數據直接從數據的指定位置操作即可,時間復雜度為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 匯編指令。有了這個操作,我們就可以用其來實現各種無鎖的數據結構。
無鎖隊列也屬於隊列的一種,所以大部分隊列的使用場景都可以使用它來代替其它有鎖隊列,無鎖隊列通過不加鎖的方式提高隊列性能。
本文地址:https://www.linuxprobe.com/understanding-lockless-queue.html