歡迎光臨
每天分享高質量文章

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個動作:

  1. 把CPU暫存器的值儲存到正在運行的執行緒背景關係中
  2. 從現有執行緒集合中選取一個執行緒準備分配
  3. 把選中執行緒背景關係中儲存的CPU暫存器值加載到CPU暫存器中

執行緒背景關係切換會對程式性能帶來很嚴重的影響,特別是切換到一個新行程的新執行緒時,很可能需要從RAM中加載代碼和資料,大家知道RAM相對於CPU高速快取太慢了。

執行緒的創建、切換及銷毀都是有著不可忽視的開銷,在追求高性能的程式中,我們應儘量少地執行緒,最優性能的執行緒數是機器CPU的核心數。當然,性能只是程式的一個方面,響應性和可靠性也是要關註的重點。

小結

並行在行程層面依賴於系統可用系統資源和CPU核心數,單核CPU的程式並行,實質上是併發;在執行緒層面則主要依賴於CPU核心數以及我們安排執行緒的方式。

後續將以.NET為例總結併發編程。

:本文關於行程和執行緒的相關內容以Windows操作系統為參考。

原文地址:https://www.cnblogs.com/chenbaoshun/p/10535374.html

已同步到看一看
赞(0)

分享創造快樂