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

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)

分享創造快樂