1. 程式人生 > >【併發程式設計】對阻塞/非阻塞、同步/非同步、併發/並行等基本概念的理解

【併發程式設計】對阻塞/非阻塞、同步/非同步、併發/並行等基本概念的理解

1. 併發與並行

併發:concurrency 並行:parallelism

開發過程中,常常會接觸併發有關的概念,比如併發計算(concurrent computing),併發系統( concurrent system),併發控制(concurrent control),併發程式設計(concurrent programming),那麼併發到底是什麼呢?

可以簡單的理解為:同時做多件事情的能力!

一個人邊走、邊唱、邊想,這就是併發;反之,要是先走、再唱、再想,就是順序(sequentially )執行,不是併發。再比如 Web 伺服器,如 Apache,nginx,能夠同時處理多個客戶端連線,就是併發處理。

併發與並行是兩個相關(related)但不同(distinct)的概念,都是“同時做多件事”,但併發關注的點是多件事被同時(一段時期)做,但只有一件事情正在執行(單核)!而並行關注的點是多個“人”在同時做事情。比如單核系統(time-sharing)能實現併發處理,但只有多核系統才能實現並行處理。

2. 阻塞與非阻塞

阻塞就是等待任務結束,在此期間,什麼也不幹!非阻塞就是不等待任務結束,繼續做後面的事!

比如,快遞員把快遞送到小區門口,打電話給客戶,等他到小區門口把快遞拿走,再送下一件快遞,這就是阻塞模式;快遞員把快遞放在小區的快遞箱中,給客戶發了簡訊,就送其它快遞了,這就是非阻塞模式。

常見的阻塞形式有,網路阻塞,磁碟讀寫阻塞等。

3. 同步與非同步

同步:synchronous 非同步:asynchronous

同步是什麼?為什麼要同步?

同步就是讓多個任務在某一階段順序執行,是併發控制的一種方式,目的是保證資料的正確性,完整性,一致性!比如資料庫中的事務機制,併發程式設計中的鎖,訊號量,條件變數等,都是實現同步的工具。

只有在併發系統中,涉及到共享資料的訪問時,才需要考慮同步問題!但是實際開發中,常常會遇到一些非必要的同步,比如 write 100M 的資料,或者建立資料連線,都是“去同步化”的目標,就需要考慮使用非同步程式設計了。

非同步就是讓順序執行的任務併發執行,那些根本不涉及共享資料訪問、邏輯獨立的任務,非同步執行更高效!

4. 併發程式設計的實現方式

1 系統層面: 程序(Process),執行緒(Thread)

現代作業系統 Unix/Linux/Windows 都是支援“多工”的作業系統,為併發程式設計提供了不同的任務模型——程序,執行緒,程式設計師可以藉助多執行緒+多程序來實現併發程式設計。

執行緒是最小的執行單元,而程序由至少一個執行緒組成,執行緒/程序排程,如何時執行,執行多久,由作業系統來決定。資料共享和併發控制是多執行緒/多程序程式設計時面臨的重要挑戰,增加了程式設計的複雜度;另外,頻繁的上下文切換,也會影響 CPU 的使用效率。

為了減少執行緒切換,會使用執行緒池來管理執行緒。

2 語言層面: Channel,Coroutine,Futures and Promises

併發程式設計的靈活度會極大的影響一門程式語言的表現力,比如 Python 中的協程、async/wait,C# 中的 Task,async/wait,Go 中的 Channel。

另外 future, promise, delay, deferred 也是程式語言中併發程式設計模型相關的術語。

5. 總結

首先,好的程式應該能充分利用有限的 CPU 資源,努力提高資源利用率,該需求可以通過多執行緒/多程序來實現。

可是,執行緒/程序是稀缺資源,數量有限,且執行緒/程序上下文切換成本高。程式中的執行緒越多,上下文切換的成本越高,CPU 有效利用率越低!另外,在多執行緒/多程序模型中,需要考慮併發控制的問題,增加了程式設計的複雜度。

那麼如何實現不依賴多執行緒/多程序的併發程式設計呢?此時,非同步程式設計就應運而生了!

一方面,非同步/非租塞通過減少對執行緒/程序的依賴,提高了 CPU 資源的有效利用率,同時避免了多執行緒/多程序中併發控制的問題;另一方面,非同步程式設計可以降低程式設計的複雜度。