無鎖併發程式設計簡談

2021-09-02 21:39:07 字數 2506 閱讀 8084

(有半年沒有寫部落格了,時間飛逝呀。。。。 :o )

這裡簡單介紹一下無鎖併發程式設計。

現在cpu的核越來越多,多執行緒、併發程式設計已經成為趨勢。一涉及併發,同步是繞不開的話題。一般的方法是使用mutex、旋轉鎖、條件變數等系統提供的方法來進行同步。(乙個問題,mutex和自旋鎖的區別?)如果併發不太大,qps是數百時,這些方法還沒有問題,但是當qps增加到數千時,這些同步方法的開銷就太大了,會對系統產生不小的開銷,影響程式的效能。怎麼辦?

一種方法是先建立一堆執行緒池,然後將query計算後(比如取模),傳給相應的執行緒處理,這樣即使使用傳統的同步方法,還是可以降低同步的開銷。然而對於全域性唯一的資源,還是不能降低同步的開銷。這樣,就引出了無鎖併發的概念。

無鎖併發(lock-free),就是不使用鎖來進行同步,那就要知道cas(compare&set或者compare&swap)的概念。其實cas很簡單,就是取出資源或者版本號後,再進行操作前,再進行比較,看期間資源或版本號有沒有改變,如果沒有,則表明沒有其他程式修改過,可以直接修改。如果當前的資料和之前的資料不一樣,則表明期間資源被修改過,則之前的資源位址後者版本號已經失效,需要重新獲取,之後再次比較和操作。

要進行cas,要了解[color=darkred]gcc內建[/color]提供的一系列原子操作的函式:

type __sync_fetch_and_add (type *ptr, type value, ...)

type __sync_fetch_and_sub (type *ptr, type value, ...)

type __sync_fetch_and_or (type *ptr, type value, ...)

type __sync_fetch_and_and (type *ptr, type value, ...)

type __sync_fetch_and_xor (type *ptr, type value, ...)

type __sync_fetch_and_nand (type *ptr, type value, ...)

type __sync_add_and_fetch (type *ptr, type value, ...)

type __sync_sub_and_fetch (type *ptr, type value, ...)

type __sync_or_and_fetch (type *ptr, type value, ...)

type __sync_and_and_fetch (type *ptr, type value, ...)

type __sync_xor_and_fetch (type *ptr, type value, ...)

type __sync_nand_and_fetch (type *ptr, type value, ...)

這兩組函式的區別在於第一組返回更新前的值,第二組返回更新後的值。

type可以是1,2,4或8位元組長度的int型別,即:

int8_t / uint8_t

int16_t / uint16_t

int32_t / uint32_t

int64_t / uint64_t

後面的可擴充套件引數(...)用來指出哪些變數需要memory barrier,因為目前gcc實現的是full barrier(類似於linux kernel 中的mb(),表示這個操作之前的所有記憶體操作不會被重排序到這個操作之後),所以可以略掉這個引數。

bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)

type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)

這兩個函式提供原子的比較和交換,如果*ptr == oldval,就將newval寫入*ptr,

第乙個函式在相等並寫入的情況下返回true.

第二個函式在返回操作之前的值。

__sync_synchronize (...)

發出乙個full barrier.

這樣,就可以利用這些內建的函式,開發乙個無鎖的stack:

template

class stack

} node;

node *top;

public:

stack( ) : top(0)

void push(const t& data);

t pop( ) throw (…);

};void stack::push(const t& data) }}

t stack::pop( ) }}

嗯,基本就是這麼簡單,當然還有一些複雜的場景需要探索,要在使用中摸索。

其實想一想,cas最終還是用到了「鎖」,不過這個鎖是程式語言控制的最小的同步鎖,所以顆粒是最小的,對系統的開銷也是最小的。

後記:無鎖程式設計中乙個很大的問題是aba問題(變數中間被修改後又復原了),可能造成程式的隱患,所以無鎖程式設計侷限性還是很大的。

參考:[url]

[url]

簡談網路程式設計

網路程式設計的本質是兩個裝置之間的資料交換。乙個裝置中的資料傳送給兩外乙個裝置,然後接受另外乙個裝置反饋的資料。現在的網路程式設計基本上都是基於請求 響應方式的,也就是乙個裝置傳送請求資料給另外乙個,然後接收另乙個裝置的反饋。傳送第一次請求的程式,被稱作客戶端 client 等待其他程式連線的程式被...

無鎖程式設計 大綱

鎖定被迫交出時間片。鎖定意味著阻塞,多個執行緒 程序 排隊獲取資源,無法充分發揮系統效能。鎖定的阻塞無法通過fd進行通知,對效能有進一步的影響 理想的伺服器模型是全域性一處阻塞統一等待訊息 一些鎖限制了必須使用執行緒的方式進行開發,而執行緒無法充分利用系統的記憶體。pthread庫在特殊情況下可能產...

併發無鎖佇列學習(一)

1 概述 佇列在計算機中非常重要的一種 資料結構 尤其在 作業系統 中。佇列典型的特徵是先進先出 fifo 符合流水線業務流程。在程序間通訊 網路通訊之間經常採用佇列做快取,緩解資料處理壓力。結合自己在工作中遇到的佇列問題,總結一下對不同場景下的佇列實現。根據操作佇列的場景分為 單生產者 單消費者 ...