C#并行编程(1):理解并行
什么是并行
并行是指兩個或者多個事件在同一時刻發生。
在程序運行中,并行指多個CPU核心同時執行不同的任務;對于單核心CPU,嚴格來說是沒有程序并行的。并行是為了提高任務執行效率,更快的獲取結果。
與并發的區別:
并發是指兩個或者多個事件在同一時段發生。
相對于并行,并發強調的是同一時段,是宏觀上的同時發生。實際上,同一時刻只有一個任務在被執行,多個任務是分時地交替執行的。并發是為了更合理地分配資源。
如何實現并行
并行編程中我們只關注應用層面的并行,CPU的指令并行技術(指令流水等)不在我們的考慮范圍。
從并行的意義來看,并行編程的目的無非是讓多個CPU核心同時執行不同業務邏輯,獲取優良的性能。但是,要怎樣實現并行呢?實現并行,我們要借助進程和線程。
為了更好地管理計算機中運行的程序,計算機操作系統引入進程:
狹義定義:進程是正在運行的程序的實例(an?instance?of?a?computer?program?that?is?being?executed)。
廣義定義:進程是一個具有一定獨立功能的程序關于某個數據集合的一次運行活動。
——百度百科
由于進程擁有計算機資源,在創建、切換和撤銷的過程中開銷較大,這就限制了進程的并發程度;多核CPU的日漸普及的環境下,為提高并行粒度和并行計算的效率,引入了一種輕型的進程——線程:
線程(英語:thread)是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。
——百度百科
線程包含于進程,同一進程的線程共享該進程的資源。線程出現后,線程取代進程作為操作系統調度和分派的基本單位,極大地減少了進程切換帶來的性能損失,使得更細粒度和更高性能的并行得以實現。
進程的調度
一臺計算機會運行很多程序,這些程序進程的數量多會大于CPU的核心數量。每個CPU核心同一時間只能執行一個進程,那操作系統是如何管理這些進程的呢?
當啟動一個程序的實例時,操作系統將創建一個進程用來調度該程序實例。一個進程主要包含以下的信息:
進程控制塊PCB,用于操作系統控制該程序實例
進程標識信息,如PID、名稱等
現場信息,存放進程運行時處理器現場信息
控制信息,存放操作系統用于管理和調度進程的信息
專有的虛擬地址空間
句柄列表
程序實例的代碼和數據,被映射到進程私有虛擬地址空間
程序狀態字信息
進程的狀態模型,如下圖:
操作系統按照進程狀態進行程序調度。
啟動程序時,操作系統創建進程,此時進程為新建態
運行資源充足時,操作系統提交進程到就緒狀態,等待CPU選擇或者搶占CPU執行
運行資源不足,如主存不夠,操作系統會掛起進程,進程狀態改為就緒掛起,等待操作系統的恢復
就緒狀態的進程
CPU空閑時,會選擇執行就緒狀態的進程,被選中的進程進入運行狀態
進程優先級高時,將搶占當前正在執行進程的CPU資源,自身進入運行狀態
操作系統會根據當前的可用資源,把就緒狀態的進程掛起
就緒掛起的進程
當前沒有就緒的進程,或者就緒掛起的某個進程具有較高的優先級,操作系統會將就緒掛起的進程恢復到就緒狀態
運行狀態的進程
進程自然結束、被強制終結或者出現無法解決的異常,將進入終止狀態,終止的線程不再參與進程調度
進程到達運行的時間片或者出現優先級高的進程搶占了CPU,進程會回到就緒狀態等待調度
進程等待資源、I/O或者信號時,會進入阻塞狀態
優先級較高的進程搶占CPU,而此時系統資源不足,則正在運行的線程會被轉入就緒掛起狀態
阻塞狀態的進程
進程阻塞的條件被滿足,如等待的資源到位、I/O完成或收到信號,會進入就緒狀態
進程在等待資源、I/O或者信號時,若系統檢測到運行資源不足,會將阻塞的進程掛起進入阻塞掛起狀態
阻塞掛起的進程
當被掛起的進程具有較高優先級,同時由于其他進程的退出使資源充裕,進程會被轉為阻塞狀態
掛起的阻塞進程得到資源、I/O完成或者收到信號后,被轉入就緒掛起狀態
上述便是進程的調度過程,其中掛起的進程不占有任何資源。進程的調度很大程度是依賴于運行資源的;進程的優先級也是影響進程調度的重要因素;此外進程的調度還會涉及進程間的通信和同步問題,這里不做展開。
實際上,相對于進程,在并行編程中我們更關心線程,因為線程才是系統調度的基本單位。
線程的調度
在Windows系統中,每個進程至少有一個線程,每個線程都包含下面的內容:
線程內核對象,包含線程上下文(包含CPU寄存器信息的內存塊)
線程環境塊,包含線程的異常處理鏈首、本地存儲數據等
用戶模式棧,存儲傳給方法的局部變量和實參
內核模式棧,線程調用操作系統內核函數時,所傳實參從用戶模式棧復制到內核模式棧
DLL線程連接和分離,線程創建和銷毀時,所依賴的DLL需要收到通知才能執行相關資源的初始化和清理
從線程所含內容,我們可以知道線程的創建和銷毀是有著時間和空間開銷的,雖然這些開銷相較于進程來說小了很多,但仍是影響程序效率的重要因素。特別是在并行處理的時候,線程的頻繁創建和銷毀將對并行性能產生極為嚴重的影響。
系統同一時間只給一個CPU核心分配一個線程,CPU執行該線程達一個時間片后,系統會給該CPU核心分配另一個線程。系統分配線程至CPU核心的過程就是線程的上下文切換過程,此間,系統將執行3個動作:
把CPU寄存器的值保存到正在運行的線程上下文中
從現有線程集合中選取一個線程準備分配
把選中線程上下文中保存的CPU寄存器值加載到CPU寄存器中
線程上下文切換會對程序性能帶來很嚴重的影響,特別是切換到一個新進程的新線程時,很可能需要從RAM中加載代碼和數據,大家知道RAM相對于CPU高速緩存太慢了。
線程的創建、切換及銷毀都是有著不可忽視的開銷,在追求高性能的程序中,我們應盡量少地線程,最優性能的線程數是機器CPU的核心數。當然,性能只是程序的一個方面,響應性和可靠性也是要關注的重點。
小結
并行在進程層面依賴于系統可用系統資源和CPU核心數,單核CPU的程序并行,實質上是并發;在線程層面則主要依賴于CPU核心數以及我們安排線程的方式。
后續將以.NET為例總結并發編程。
注:本文關于進程和線程的相關內容以Windows操作系統為參考。
原文地址:https://www.cnblogs.com/chenbaoshun/p/10535374.html
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?
總結
以上是生活随笔為你收集整理的C#并行编程(1):理解并行的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Core使用Jaeger
- 下一篇: Orleans MultiClient