認識無鎖佇列

roc_guo發表於2021-06-23

對於多執行緒使用者來說,無鎖佇列的入隊和出隊操作是執行緒安全的,不用再加鎖控制。

什麼是無鎖佇列

佇列每個開發者都知道,那麼什麼又是無鎖佇列呢?字面理解起來就是一個無鎖狀態的佇列,多個執行緒(消費者)同時運算元據的時候不需要加鎖,因為加/解鎖都是一個很消耗資源的動作。

資料結構

我們先看一下無鎖佇列的底層實現資料結構。

無鎖佇列底層的資料結構實現方式主要有兩種:陣列 和 連結。

陣列

在首次初始化時,需要申請一塊連線的大的記憶體。讀寫資料直接從資料的指定位置操作即可,時間複雜度為O(1)。

缺點:陣列長度有限,一旦陣列索引位置寫滿,則無法繼續寫入,即佇列有上限。

連結串列

不用像陣列一樣,剛開始就申請一塊連線的大的記憶體空間。只有在每次寫時資料的時候,申請這個資料節點大小的記憶體即可,這樣就可以實現無限的寫入,沒有長度限制問題。

缺點:每次寫資料都要申請記憶體,在寫的場景,最差的情況是多少個資料就申請多少次記憶體,而每次申請都是一個消耗資源的動作。

可以看到無鎖底層的實現的不同各有優勢。多資料情況下,我們都採用連結串列來實現無鎖佇列,主要原因就是寫入可以沒有長度的限制。相比每次申請都要費時來說,滿足前面的條件是我們最基本的要求。當然主要還是真正的使用場景。

CAS

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/,如需轉載,請註明出處,否則將追究法律責任。

相關文章