第十三周学习报告
第十二章 并發編程
如果邏輯控制流在時間上重疊,那么它們就是并發的。
使用應用級并發的應用程序稱為并發程序,三種基本的構造并發程序的方法:進程、I/O多路復用、線程
12.1基于進程的并發編程
第一步:服務器接受客戶端的連接請求
第二步:服務器派生一個子程序為這個客戶端服務
第三步:服務器接受另一個連接請求
第四步:服務器派生另一個子程序為新的客戶端服務
12.1.1 基于進程的并發服務器
必須要包括一個SIGCHLD處理程序,來回收僵死子程序的資源
為避免存儲器泄露,必須關閉各自的connfd拷貝
直到父子進程的connfd都關閉了,到客戶端的的連接才會終止
12.1.2 關于進程的優劣
父子進程間的共享狀態信息模型:共享文件表,但是不共享用戶地址信息
有獨立的地址空間:優點:一個進程不可能不小心覆蓋另一個進程的虛擬存儲器
缺點:共享狀態信息變得困難,必須使用顯式的IPC機制;比較慢
?
12.2 基于I/O多路復用的并發編程
服務器必須響應兩個相互獨立的I/O事件
基本思路:使用select函數,要求內核掛起進程
Select函數有兩個輸入:1.讀集合的描述符集合 2.基數n
當且僅當一個從該描述符讀取一個字節的請求不會阻塞時,描述符k就表示準備好可以讀了
12.2.1 基于I/多路復用的并發事件驅動服務器
I/O多路復用可以用做并發事件驅動程序的基礎
一個狀態機就是一組狀態、輸入事件和轉移
自循環是同一輸入和輸出狀態之間的轉移
活動客戶端的集合維護在一個pool結構里
在通過調用init_pool初始化池之后,服務器進入一個無限循環
在循環的每次迭代中,服務器調用select函數來檢測兩種不同類型的輸入事件:
Init_pool函數初始化客戶端池
Add_client函數添加一個新的客戶端到活動客戶端池中
Check_clients函數回送來自每個準備好的已連接描述符的一個文本行
優點:1.比基于進程的設計給了程序員更多對程序行為的控制
2.每個邏輯流都能訪問該進程的全部地址空間
缺點:1.編碼復雜 2.不能充分利用多核處理器
?
12.3基于線程的并發編程
線程就是運行在進程上下文中的邏輯流
每個線程都有它自己的線程上下文,包括一個唯一的整數線程ID、棧、棧指針、程序計數器、通用目的寄存器和條件碼
所有運行在一個進程里的線程共享該進程的整個虛擬地址空間
12.3.1線程執行模型
每個進程開始生命周期時都是單一線程,這個線程稱為主線程
在某一時刻,線程創建一個對等線程,從這個時間點開始,兩個線程就并發地運行
最后,因為主線程執行一個慢速系統調用,控制就會通過上下文切換傳遞到對等線程
對等線程執行一段時間,然后控制傳遞回主線程
12.3.2Posix線程
Posix線程是在C程序中處理線程的一個標準接口
Pthreads運行程序創建、殺死和回收線程,與對等線程安全地共享數據
12.3.3創建線程
Pthread_create函數
創建一個新的線程,并帶著一個輸入變量arg
在新線程的上下文中運行線程例程f
當其返回時,參數tid包含新創建線程的ID
12.3.4終止線程
方法:1.頂端線程例程返回(隱式)
2.調用pthread_exit函數(顯式)
3.某個對等線程調用Unix的exit函數
4.另一個對等線程通過以當前線程ID作為參數調用pthread_cancle函數
12.3.5 回收已終止線程的資源
調用pthread_join函數等待其他線程終止
會阻塞,直到線程tid終止,將線程例程返回的(void*)指針賦值為thread_return指向的位置,然后回收已終止線程占用的所有存儲器資源
12.3.6分離線程
Pthread_detach分離可結合線程tid
線程能夠通過以pthread_self()為參數的pthread_detach調用來分離它們
12.3.7 初始化進程
Pthread_once
?
12.4多線程程序中的共享變量
12.4.1 線程存儲器模型
一組并發進程運行在一個進程的上下文中
每個線程都有它自己獨立的線程上下文
12.4.2 將變量映射到存儲器
全局變量是定義在函數之外的變量
本地自動變量就是定義在函數內部但是沒有static屬性的變量
本地靜態變量是定義在函數內部并有static屬性的變量
12.4.3 共享變量
一個變量v是共享的,當且僅當它的一個實例被一個以上的變量引用
?
12.5 用信號量同步線程
一般而言,沒有辦法預測操作系統是否將為線程選擇一個正確的順序,可以借助進度圖來闡明
12.5.1 進度圖
進度圖將n個并發線程的執行模型化為一條n維笛卡兒空間中的軌跡線
每條軸k對應于進程k的進度
每個點I代表進程已經完成了指令I這一狀態
圖的原點對應于沒有任何線程完成一條指令的初始狀態
進度圖將指令執行模型化為一種狀態到另一種狀態的轉換
合法的轉換是向右或者向上的
操作共享變量cnt內容的指令(L,U,S)構成了一個臨界區
兩個臨界區的交集稱為不安全區
繞開不安全區的軌跡線叫做安全軌跡線,反之,叫做不安全軌跡線
?
12.5.2 信號量
信號量s是具有非負整數的全局變量,只能由兩種操作處理。
P(s):如果s是非零的,那么P將s減1返回,如果s為零,就掛起
V(s):將s加1,如果有任何線程阻塞在P操作等待s變成非零,那么V操作會重啟線程中的一個,然后s減1
?
12.5.3 使用信號量來實現互斥
基本思想:將每個共享變量與一個信號量s聯系起來,用P和V操作將臨界區包圍起來
二元信號量的值總是0或1
以提供互斥為目的的信號量也稱為互斥鎖
P:加鎖 V:解鎖
一個被用作一組可用資源的計數器的信號量稱為計數信號量
關鍵思想:創建禁止區
?
12.7 其他并發問題
12.7.1 線程安全
四個線程不安全函數類:
12.7.2 可重入性
可重入函數:當它們被多個線程調用時,不會引用任何共享數據
12.7.3 在線程化的程序中使用已存在的庫函數
見p693圖12-39
除了rand和strtok,所以這些線程不安全函數都是第3類的
12.7.4 競爭
當一個程序的正確性依賴于一個線程要在另一個線程到達y點之前到達它的控制流中的x點,就會發生競爭
12.7.5 死鎖
信號量引入運行時的錯誤,叫做死鎖
一組線程被阻塞,等待一個永遠也不會為真的條件
互斥鎖加鎖順序規則:如果對于程序中每對互斥鎖(s,t),給所有的鎖分配一個全序,每個線程按照這個順序來請求鎖,并且按照逆序來釋放,那么這個程序就是無死鎖的。
參考資料:《深入理解計算機系統》
?
?
轉載于:https://www.cnblogs.com/javablack/p/5024192.html
總結
- 上一篇: scala类型推断及库方法设计原则和==
- 下一篇: [转]JS调用Android里面的方法,