標籤:.net select copy 令行 異常 join 詳細說明 全域 ble
該書中第11章是寫web伺服器的搭建,無奈對web還比較陌生。還沒有搞明白。
這些所謂的並發,其實都是作業系統做的事情,比如,多進程是作業系統fork函數實現的、I/O多工需要核心掛起進程、多線程需要核心建立和掛起線程。我麼只是使用以下作業系統的這項並發技術。但是我們必須處理一些存在問題。
●進程。用這種方法,每個邏輯控制流程都是一個進程,由核心來調度和維護。因為進程有獨立的虛擬位址空間,想要和其他流通訊,控制流程必須使用處理序間通訊(IPC)。
●I/O多工。這種形式的並發,應用程式在一個進程的上下文中顯示地調度它們自己的邏輯流。邏輯流被類比為“狀態機器”,資料到達檔案描述符後,主程式顯示地從一個狀態轉換到另一個狀態。因為程式是一個單獨的進程,所以所有的流都共用一個地址空間。
●線程。線程是運行在一個單一進程上下文中的邏輯流,由核心進行調度。線程可以看做是進程和I/O多工合體,像進程一樣由核心調度,像I/O多工一樣共用一個虛擬位址空間。
12.1 基於進程的並發編程
構造並發最簡單的就是使用進程,像fork函數。例如,一個並發伺服器,在父進程中接受用戶端串連請求,然後建立一個新的字進程來為每個新用戶端提供服務。
關於進程的優劣,對於在父、子進程間共用狀態資訊,進程有一個非常清晰的模型:共用檔案表,但是不共用使用者地址空間。進程有獨立的地址控制項愛你既是優點又是缺點。由於獨立的地址空間,所以進程不會覆蓋另一個進程的虛擬儲存空間。但是另一方面處理序間通訊就比較麻煩,至少開銷很高。
12.2基於I/O多工並發編程
比如一個伺服器,它有兩個I/O事件:1)網路用戶端發起串連請求,2)使用者在鍵盤上鍵入命令列。我們先等待那個事件呢?沒有那個選擇是理想的。如果accept中等待串連,那麼無法相應輸入命令。如果在read中等待一個輸入命令,我們就不能響應任何串連請求(這個前提是一個進程)。
針對這種困境的一個解決辦法就是I/O多工技術。基本思想是:使用select函數,要求核心掛起進程,只有在一個或者多個I/O事件發生後,才將控制返給應用程式。:橫向的方格可以看作是一個n位的描述符向量。現在,我們定義第0位描述是“標準輸入”,第3位描述符是“監聽描述符”。
I/O多工優劣:由於I/O多工是在單一進程的上下文中的,因此每個邏輯流程都能訪問該進程的全部地址空間,所以開銷比多進程低得多;缺點是編程複雜度高。
12.3基於線程的並發編程
每個線程都有自己的線程上下文,包括一個線程ID、棧、棧指標、程式計數器、通用目的寄存器和條件碼。所有的運行在一個進程裡的線程共用該進程的整個虛擬位址空間。由於線程運行在單一進程中,因此共用這個進程虛擬位址空間的整個內容,包括它的代碼、資料、堆、共用庫和開啟的檔案。所以我認為不存線上程間通訊,線程間只有鎖的概念。
線程執行的模型。線程和進程的執行模型有些相似。每個進程的聲明周期都是一個線程,我們稱之為主線程。但是大家要有意識:線程是對等的,主線程跟其他線程的區別就是它先執行。
一般來說,線程的代碼和本機資料被封裝在一個線程常式中(就是一個函數)。該函數通常只有一個指標參數和一個指標傳回值。
在Unix中線程可以是joinable(可結合)或者detached(分離)的。joinable可以被其他線程殺死,detached線程不能被殺死,它的儲存空間資源有系統自動釋放。
線程儲存空間模型,每個線程都有它自己的獨立的線程上下文,包括線程ID、棧、棧指標、程式計數器、條件碼和通用目的寄存器。每個線程和其他線程共用剩下的部分,包括整個使用者虛擬位址空間,它是由程式碼片段、資料區段、堆以及所有的共用庫代碼和資料區域組成。不同線程的棧是對其他線程不設防的,也就是說:如果一個線程以某種方式得到一個指向其他線程的指標,那麼它可以讀取這個線程棧的任何部分。
12.4多線程中共用變數
●全域變數和static 變數是儲存在資料區段,所以,多線程共用之!
●由於線程的棧是獨立的,所有線程中的自動變數是獨立的。即使多個線程運行同一段代碼總的自動變數,那麼他們的值也是根據線程的不同而不同。
●比如C++中,類屬性不是在使用者棧中的。所以線程共用之!
12.5用訊號量同步線程。
訊號量通常稱之為PV操作(發明PV操作的是荷蘭一個哥們),雖然它的思想是將臨界代碼保護起來,達到互斥效果。這裡面作業系統使用到了線程掛起!PV操作是就不再做過多的解析。下面展示一個多線程對共用變數修改的進度圖的解釋:
現在有兩個線程thread1和thread2.那麼作業系統並行thread1 和thread2。彙編的執行順序如下(可能的順序,一共是10步):
H1、L1、U1、S1、T1、H2、L2、U2、S2、T2//可以得到cnt =2
H1、L1、U1、H2、L2、S1、T1、U2、S2、T2//得到錯誤的cnt =1
那麼,這種排列組合會有很多種情況。這十步會給我們帶來奇妙的答案。下面我們用進度圖(是一種二維笛卡爾座標系)來表達:
黃色的地區是臨界區,綠色的軌跡是不安全的,而藍色的軌跡是安全的。線程鎖就是當線程即將進入臨界區的時候,掛起自己等待其他線程走完臨界區,自己再執行。當然,二維笛卡爾已經不能描述線程鎖的原理了。
剩餘章節還介紹了“生產者——消費者”問題,“讀者——寫者”問題,略過。
12.6 使用線程提高並行性
12.7安全執行緒 我們編程過程中,儘可能編寫安全執行緒函數,即一個函數若且唯若被多個並發線程反覆調用時,它會一直產生正確的結果。如果做不到這個條件我們稱之為線程不安全函數。下面介紹四類線程不安全函數: ●不保護共用變數的函數。解決辦法是PV操作。 ●保持跨越多個調用的狀態函數。比如使用靜態變數的函數。解決方案是不要使用靜態變數或者使用可讀靜態變數。 ●返回指向靜態變數的指標的函數。解決方案是lock-and-copy(枷鎖-拷貝) ●調用線程不安全函數的函數
死結。由於PV操作不當,可能造成死結現象。這在程式中也會出現。是很頭疼的事情。
總結
無亂那種並發機制,同步對共用資料的並發都是一個困難的問題。該書沒有更詳細說明並行程式的過程。但是我相信:只要作業系統有掛起的功能,那麼並發的形式應該是多種多樣的!
《深入理解電腦系統》昨天已經看完了,今天把最後一章存檔為部落格,方便以後自己查閱並加深理解,這篇是終結篇!
縱覽該書,其重要程度不言而喻。囊括了二進位表示、彙編、指令、快取、CPU體繫結構、儲存空間、虛擬儲存空間、棧、堆、異常、進程、編譯、連結、靜態庫和動態庫、運行、網路、線程和並發。可以作為從事電腦行業著知識的基石,只恨自己大學時候沒有讀這本書。
原文:http://blog.csdn.net/hherima/article/details/8987813
4.2《深入理解電腦系統》筆記(五)並發、多進程和多線程【Final】