併發編程的術語 併發 同時做多件事情 多線程 併發的一種形式,它採用多個線程來執行程式。 多線程是併發的一種形式,但不是唯一的形式。 並行處理 把正在執行的大量的任務分割成小塊,分配給多個同時運行的線程。 並行處理是多線程的一種,而多線程是併發的一種。 非同步編程 併發的一種形式,它採用future模 ...
併發編程的術語
- 併發
同時做多件事情 - 多線程
併發的一種形式,它採用多個線程來執行程式。
多線程是併發的一種形式,但不是唯一的形式。 - 並行處理
把正在執行的大量的任務分割成小塊,分配給多個同時運行的線程。
並行處理是多線程的一種,而多線程是併發的一種。 - 非同步編程
併發的一種形式,它採用future模式或回調(callback)機制,以避免產生不必要的線程。
一個 future(或 promise)類型代表一些即將完成的操作。在 .NET 中,新版 future 類型有 Task 和 Task。在老式非同步編程 API 中,採用回調或事件(event),而不是future。非同步編程的核心理念是非同步操作(asynchronous operation):啟動了的操作將會在一段時間後完成。這個操作正在執行時,不會阻塞原來的線程。啟動了這個操作的線程,可以繼續執行其他任務。當操作完成時,會通知它的 future,或者調用回調函數,以便讓程式知道操作已經結束。 - 響應式編程
一種聲明式的編程模式,程式在該模式中對事件做出響應。
響應式編程的核心理念是非同步事件(asynchronous event):非同步事件可以沒有一個實際的“開始”,可以在任何時間發生,並且可以發生多次,例如用戶輸入。
如果把一個程式看作一個大型的狀態機,則該程式的行為便可視為它對一系列事件做出響應,即每換一個事件,它就更新一次自己的狀態。
非同步編程的兩個好處
- 對於面向終端用戶的 GUI 程式:非同步編程提高了響應能力。面對在運行時被臨時鎖定界面的程式,非同步編程可以使程式在此時仍能流暢的響應用戶的輸入。譬如:WPF界面,執行一個需要等待的操作時,仍可以點擊輸入框進行填寫,而不會出現卡頓,無法點擊的情況或者對頁面無法進行拖拽。
- 對於伺服器端應用:非同步編程實現了可擴展性。伺服器應用可以利用線程池滿足其可擴展性,使用非同步編程後,可擴展性通常可以提高一個數量級。即提高伺服器端應用的TPS(Transactions Per Second)和 QPS (Queries Per Second)
並行的兩種形式
並行編程的使用場景:需要執行大量的計算任務,並且這些任務能分割成相互獨立的任務塊兒
並行的形式有兩種:數據並行(data parallelism)和任務並行(task parallelim)。
數據並行(data parallelism):有大量的數據需要處理,並且每一塊數據的處理過程基本上是彼此獨立的。
任務並行(task parallelim):需要執行大量任務,並且每個任務的執行過程基本上是彼此獨立的。任務並行可以是動態的,如果一個任務的執行結果會產生額外的任務,這些新增的任務也可以加入任務池。
實現數據並行的方法
- Parallel.ForEach
- PLINQ(Parallel LINQ)
每個任務塊要儘可能的互相獨立。 只要任務塊是互相獨立的,並行性就能做到最大化。一旦你在多個線程中共用狀態,就必須以同步方式訪問這些狀態,那樣程式的並行性就變差了。
數據並行重點在處理數據,任務並行則關註執行任務。
實現任務並行的方法
- Parallel.Invoke
- Task.Wait
通常情況下,沒必要關心線程池處理任務的具體做法。數據並行和任務並行都使用動態調整的分割器,把任務分割後分配給工作線程。線程池在需要的時候會增加線程數量。線程池線程使用工作竊取隊列(work-stealing queue)。
響應式編程Rx學習難度較大
使用場景:處理的事件中帶有參數,最好採用響應式編程
響應式編程的核心概念是:可觀察的流(observable stream)
響應式編程的最終代碼非常像 LINQ,可以認為它就是“LINQ to events”,它採用“推送”模式,事件到達後就自行穿過查詢。
TPL數據流
非同步編程和並行編程這兩種技術結合起來就是TPL數據流
數據流網格的基本組成單元是數據流塊(dataflow block)。
Rx 和 TPL有很多相同點。
網格和流都有“數據項”這一概念,數據項從網格或流的中間穿過。還有,網格和流都有“正常完成”(表示沒有更多數據需要接收時發出的通知)和“不正常完成”(在處理數據中發生錯誤時發出的通知)這兩個概念。但是,Rx 和 TPL 數據流的性能並不相同。
當需要執行需要計時的任務,最佳選擇是Rx的 可觀察流 observable 對象
當需要進行並行處理,最佳選擇是 TPL數據流塊
線程和線程池
線程是一個獨立的運行單元,每個進程內部有多個線程,每個線程可以各自同時執行指令。每個線程有自己獨立的棧,但是與進程內的其他線程共用記憶體。
對某些程式來說,其中有一個線程是特殊的,例如用戶界面程式有一個 UI 線程,控制台程式有一個 main 線程。
每個 .NET 程式都有一個線程池,線程池維護著一定數量的工作線程,這些線程等待著執行分配下來的任務。線程池可以隨時監測線程的數量。配置線程池的參數多達幾十個,但是建議採用預設設置,線程池的預設設置是經過仔細調整的,適用於絕大多數現實中的應用場景。
併發編程的設計原理
大多數併發編程技術有一個類似點:它們本質上都是函數式(functional)的。函數式編程理念是併發編程的本質。