1. 程式人生 > 其它 >《Unix/Linux系統程式設計》第四章學習筆記

《Unix/Linux系統程式設計》第四章學習筆記

學習筆記

20191318 王澤文

  《Unix/Linux系統程式設計》


第四章 併發程式設計

  本章論述了併發程式設計,介紹了平行計算的概念,指出了平行計算的重要性;比較了順序演算法與並行演算法, 以及並行性與併發性;解釋了執行緒的原理及其相對於程序的優勢;解釋了死鎖問題, 並說明了如何防止併發程式中的死鎖問題;討論了訊號量, 並論證了它們相對千條件變數的優點;還解釋了支待 Linux 中執行緒的獨特方式。


  平行計算

  基於分治原則(如二叉樹查詢和快速排序等)的演算法經常表現出高度的並行性,可通過使用並行或併發執行米提高計算速度。平行計算是一種計算方案,它嘗試使用多個執行並行演算法的處理器 更快速地解決問題。

  井行性與併發性

  並行演算法只識別可並行執行的任務,但是它沒有規定如何將任務對映到處理元件。在理想情況下,並行演算法中的所有任務都應該同時實時執行。然而,真正的並行執行只能在有多個處理元件的系統中實現,比如多處理牉或多核系統。在單CPU系統中,一次只能執行一個任務。在這種情況下,同的任務只能併發執行,即在邏輯上並行執行。在單CPU系統中併發性是通過多工處理來實現的。


  執行緒

  執行緒是某程序同一地址空間上的獨立執行單元。建立某個程序就是在一個唯一地址空間建立一個執行緒。當某程序開始時,就會執行該程序的主執行緒。如果只有一個主執行緒,那麼程序和執行緒實 際上並沒有區別。但是, 主執行緒可能會建立其他執行緒。 每個執行緒又可以建立更多的執行緒等。 某程序的所有執行緒都在該程序的相同地址空間中執行, 但每個執行緒都是一個獨立的執行單元。

  執行緒優點

  • 執行緒建立和切換速度更快:若要在某個程序中建立執行緒,作業系統不必為新的執行緒分配記憶體和建立頁表,因為執行緒與程序共用同一個地址空間。所以,建立執行緒比建立程序更快。
  • 執行緒的響應速度更快:一個程序只有一個執行路徑。當某個程序被掛起時,幫個程序都將停止執行。相反,當某個執行緒被掛起時,同一程序中的其他執行緒可以繼續執行。
  • 執行緒更適合井行計算:平行計算的目標是使用多個執行路徑更快地解決間題。基於分治原則(如二叉樹查詢和快速排序等)的演算法經常表現出高度的並行性,可通過使用並行或併發執行來提高計算速度。

  執行緒缺點

  • 由於地址空間共享,執行緒需要來自使用者的明確同步。
  • 許多庫函式可能對執行緒不安全
  • 在單CPU系統上,使用執行緒解決間題實際上要比使用順序程式慢,這是由在執行時建立執行緒和切換上下文的系統開銷造成的。

  執行緒操作

  執行緒的執行軌跡與程序類似。執行緒可在核心模式或使用者模式下執行。在使用者模式下,執行緒在程序的相同地址空間中執行,但每個執行緒都有自己的執行堆疊。執行緒是獨立的執行單元,可根據作業系統核心的排程策略,對核心進行系統呼叫,變為桂起啟用以繼續執行等。為了利用執行緒的共享地址空間,作業系統核心的排程策略可能會優先選擇同一程序中的執行緒,而不是不同程序中的執行緒。


  Pthread併發程式設計

Pthread庫提供了用於執行緒管理的以下APT。

pthread_create(thread, attr, function, arg): create thread 
pthread_exit(status):terminate thread 
pthread_cancel(thread) : cancel thread 
pthread_attr_init(attr) : initialize thread attributes 
pthread_attr_destroy(attr): destroy thread attribute 
  • 使用pthread_ create()函式建立執行緒。int pthread_create (pthread_t *pthread_id,pthread_attr_t•attr,void * (*func) (void *), void *arg); 如果成功則返回0,如果失敗則返回錯誤程式碼。
    • pthread_id是指向pthread_t型別變員的指標。它會被作業系統核心分配的唯一執行緒ID填充。在POSIX中,pthread_t是一種不透明的型別。程式設計師應該不知道不透明物件的內容,因為它可能取決千實現情況。執行緒可通過pthread_self()函式獲得自己的ID。在Linux中,pthread_t型別被定義為無符號長整型,因此執行緒ID可以列印為%lu。
    • attr是指向另一種不透明資料型別的指標,它指定執行緒屬性。
    • func是要執行的新執行緒函式的人口地址。
    • arg是指向執行緒函式引數的指標,可寫為:void *func(void *arg) ,attr引數使用:
      • 定義一個pthread展性變址pt:hread_attr_tattr。
      • 用pthread_attr_init(&attr)初始化屈性變掀。
      • 設定屬性變垃並在pthread_ create()呼叫中使用。
      • 必要時,通過pthread_attr_destroy(&attr)釋放attr資源。
  • 執行緒ID 是一種不透明的資料型別,取決於實現悄況。因此,不應該直接比較執行緒ID。如果需要,可以使用pthread_ equal()函式對它們進行比較。

int pthread_equal (pthread_t tl, pthread_t t2);

  • 執行緒函式結束後,執行緒即終止。或者,執行緒可以呼叫函式 int pthraad_axit {void *status)
  • 一個執行緒可以等待另一個執行緒的終止, 通過:int pthread_join (pthread_t thread, void **status__ptr); 終止執行緒的退出狀態以status_ptr返回。
  • 在 Pthread中,鎖被稱為互斥量,意思是相互排斥。互斥變呈是用 ptbread_mutex_t 型別宣告的在使,用之前必須對它們進行初始化。有兩種方法可以初始化互斥址。
    • 靜態方法,pthreaa—mutex_t m = PTHREAD_MUTEX_INITIALIZER; 定義互斥量 m, 並使用預設屬性對其進行初始化。
    • 動態方法,使用 pthread_ mutex _init() 函式
  • 條件變數:作為鎖,互斥量僅用於確保執行緒只能互斥地訪間臨界區中的共享資料物件。在Pthread中,使用型別pthread_cond_t來宣告條件變拉,而且必須 在使用前進行初始化。與互斥變數一樣,條件變數也可以通過兩種方法進行初始化。
    • 靜態方法:pthread_cond_t con= PTHREAD_COND_INITIALIZER;定義一個條件變屾con,並使用預設屬性對其進行初始化。
    • 動態方法:使用pthread_cond_init()函式,可通過attr引數設定條件變數。為簡便起見,我們總是使用NULLattr引數作為預設屬性。

其他

程式設計實踐