1. 程式人生 > >高併發程式設計之基礎概念

高併發程式設計之基礎概念

  在向高階程式設計師進階時,多執行緒開發是比不可少的過程,而多執行緒開發也是程式開發中的比較複雜的一塊,下面就簡單介紹一些多執行緒開發的概念。

為什麼需要並行

  • 業務要求
    在一些開發中我沒也許需要去執行兩個模組,但是兩個模組之間有互相的排程,那麼這時候如果使用同步執行緒去寫的話就會變的異常的複製,程式設計師不僅僅需要去寫業務方面的邏輯,而且還需要注意模組與模組之間的排程問題,所以此時如果我們選擇使用非同步執行緒去處理的話,模組之間的排程問題可以直接交給作業系統去處理,而程式設計師只需要去關注業務方面的東西。
  • 效能
    多執行緒程式在多核cup上面效能一般是比單執行緒程式效能要好。尤其是在計算密集型程式中是得到廣泛的應用。比如圖形處理,資料探勘等等。
  • 摩爾定律失效
    摩爾定律:預計每18個月,單核cpu效能將會提高一倍。
    2004年,Intel宣佈取消4GHz計劃。
    雖然現在已經出現了主頻為4GHz的cpu,但是在更新換代方面已經 遠遠的落後於摩爾定律。單核cpu的發展已經出現了瓶頸。此時多核cpu就誕生了,由於多核cpu的出現,併發也應運而生。頂級電腦科學家唐納德·爾文·克努斯也說過“在我看來,併發這種現象或多或少是由於硬體設計者已經無計可施,他們將摩爾定律失效的責任推脫給了軟體開發者。”

同步(synchronous)和非同步(asynchronous)

同步非同步一般是說方法呼叫,同步執行方法,只有當方法執行 結束之後才進行返回,而非同步執行方法,一般是瞬間返回,但是在後臺另外啟一條執行緒去執行這個非同步方法。


下圖中,橫向箭頭即表示單執行緒,也表示一個時間軸,同步方法執行必須要等到方法執行結束,而非同步方法則瞬間返回。

併發(concurrency)和並行(parallelism)

  許多人認為併發和並行是一樣的,然而並不一樣,雖然他們的表現方式都一樣,都是多個方法一起執行。
  並行:並行表示多個方法同時執行。
  併發:並發表示在同一時間內只執行一個方法,但是多個方法之間是交替進行的,這個由作業系統進行排程。
如下圖:

  對於單cpu來說是不會出現並行的程式,只能是併發,而多核cpu來說並行每個程序去執行一個方法。

臨界區

  對於併發程式來說,臨界區表示一種公共的資源或者是共享資料,可以被多個執行緒所使用,但是每一次只能有一個執行緒去使用它,一旦被佔用則其他執行緒都必須等待。

阻塞(blocking)和非阻塞(Non-Blocking)

  阻塞與非阻塞通常是用來說執行緒之間的相互的影響。
  阻塞:如果一個執行緒佔用了臨界區,那麼其他執行緒如果需要獲取這個資源則必須在臨界區中進行等待,等待就使得這個執行緒被掛起,這就是阻塞。但是如果佔用臨界區的資源的執行緒一直不釋放資源,則其他需要獲取資源的執行緒都將不能繼續工作。
  非阻塞:非阻塞跟上述阻塞恰恰相反,它允許多個執行緒同時進入臨界區,但是需要保證不能破壞臨界區。

死鎖(deadlock)、飢餓(starvation)和活鎖(livelock)

  對於阻塞有來說,有可能是死鎖,飢餓或者活鎖造成。

  死鎖:如果有3個方法a,b,c,有三個資源A,B,C,現在a方法佔用了A資源,但是繼續執行需要獲取B資源,b方法佔用了B資源,繼續執行需要獲取C資源,c方法佔用了C資源,繼續執行需要獲取A資源,這樣就會導致3個方法都沒有辦法繼續執行,就會導致死鎖,如果想要執行下去,就要先釋放某個資源,比如不要c方法,將C資源釋放,則b方法就會繼續執行,b方法執行結束,釋放了B資源,則a方法才可以繼續執行下去。雖然死鎖不是一個好現象,但是它屬於一個靜態問題,比較好找到原因。

  活鎖:與死鎖相對應。死鎖是誰都想獲取資源,活鎖則恰恰相反,活鎖是希望對方先獲取資源,有方法a,b,資源A,B,a方法獲取了A資源,執行時需要獲取資源B,但是發現B字眼被b方法所佔用於是就釋放了A資源,而b方法則是佔用了資源B,執行時發現資源A被佔用,則釋放了資源B,當資源都釋放之後又再次去佔用,反覆如此則沒法繼續工作。如果想要繼續執行,就要先讓其中某個執行緒不進行資源釋放,這樣就不會出現阻塞了。活鎖的話如果出現則比較難查詢原因,因為它屬於一個動態的死鎖。

  飢餓:飢餓表示,如果一個執行緒的優先順序比較低,與其他幾個獲取同一資源的執行緒去競爭資源時,資源總是被分配給優先順序比較高的執行緒,則優先順序比較低的執行緒就一直獲取不到資源而被餓死。或者是,某個執行緒在做某個原子操作時總是失敗,也會導致執行緒餓死。

並行級別

  併發分為4個級別,其中只有阻塞級別是阻塞型別的,其他三個級別無障礙,無鎖,無等待都是非阻塞型別的。
  阻塞:當一個執行緒進入臨界區後,其他執行緒必須等待。屬於一種悲觀狀態,就是說,如果由多個執行緒去修改資源,肯定會把資料破壞,所以每次只能由一個執行緒去修改資源。

  無障礙:無障礙屬於一種最弱的非阻塞排程,自由出入臨界區。它屬於一種比較樂觀的狀態,如果由多個執行緒去修改資源,未必會將資料破壞,當無競爭時正常執行,但是如果有資料競爭時則會回滾按戶籍,重新進行操作。

  無鎖:無鎖也是無障礙的,前面無障礙的程式有可能多個執行緒之間競爭資料時互相影響,則會導致所有執行緒都需要重試,最後導致所有執行緒都無法繼續執行下去,而無鎖則會保證一定會有一個執行緒競爭勝利,這樣就會讓整個程式繼續執行。

  無等待:無等待也是無鎖的,它要求所有執行緒都必須在有限步內完成,所有它也是無飢餓的。無等待的程式就屬於併發程式中最高的級別,他可以保證程式流暢就執行下去。比如所有執行緒都是讀,而沒有寫,這樣就不會導致資源被破壞,所有都可以在有限步內完成。

並行的2個定律

下面說兩個與並行程式相關的2個定律

  • Amdahl定律(阿姆達爾定律)
    Amdahl定律定義了序列系統轉換成並行系統加速比和理論的上限。
    加速比定義:加速吧=優化前系統耗時/優化後系統耗時
    我們看下面例子:

  • 如果一個程式執行需要5個步驟,其中每個步驟耗時為100,這樣這個程式執行結束耗時為500.我們優化之後將其中兩個步驟使用2個程序去跑其中每個步驟為50,這時耗時為400.

這時的 加速比=優化前系統耗時/優化後系統耗時=500/400=1.25
Amdahl定律有一個公式:

公式中各個字母的含義

  1. n 表示cpu的個數,上面例子中n = 2
  2. F 表示序列的比例,上面例子中F = 0.6
  3. 1-F 表示並行的比例,上面例子中 1-F = 0.4
  4. T1 表示單個cpu執行時的的耗時
  5. Tn 表示n個cpu執行時的耗時

    將上面公式進行帶入。

    化簡得:

最後得到一個結論,增加CPU處理器個數,並不一定可以起到提高系統內並行化模組的比重,合理的增加並行處理數量,才能以最小的投入,得到最大的加速比。

  • Gustafsson定律(古斯塔夫森定律)
    說明處理器個數,序列比例和加速比之間的關係。
    優化之後的執行時間:
    優化之前的執行時間:
    加速比:
    序列比例:
    加速比:


  1. a 表示序列時間
  2. b 表示並行時間
  3. n 表示cpu個數

上述公式說明,只要程式足夠並行化,那麼加速比和CPU個數成正比,也就是說程式足夠並行化,CPU數量越多,加速比越高。

綜合上述2個公式所得,程式的加速比和序列化比例和CPU個數相關。

 

 

 

-------------------- END ---------------------

 

 

 

最後附上作者的微信公眾號地址和部落格地址 

 

 

 

公眾號:wuyouxin_gzh

 

 

 

 

 

 

 

 

 

 

Herrt灬凌夜:https://www.cnblogs.com/wuyx/