IO複用、多程序和多執行緒三種併發程式設計模型
阿新 • • 發佈:2019-02-06
I/O複用模型
I/O複用原理:讓應用程式可以同時對多個I/O埠進行監控以判斷其上的操作是否可以進行,達到時間複用的目的。在書上看到一個例子來解釋I/O的原理,我覺得很形象,如果用監控來自10根不同地方的水管(I/O埠)是否有水流到達(即是否可讀),那麼需要10個人(即10個執行緒或10處程式碼)來做這件事。如果利用某種技術(比如攝像頭)把這10根水管的狀態情況統一傳達到某一點,那麼就只需要1個人在那個點進行監控就行了,而類似與select或epoll這樣的多路I/O複用機制就好比是攝像頭的功能,它們能夠把多個I/O埠的狀況反饋到同一處,比如某個特定的檔案描述符上,這樣應用程式只需利用對應的select()或epoll_wait()系統呼叫阻塞關注這一處即可。
I/O多路複用的優劣:由於I/O多路複用是在單一程序的上下文中的,因此每個邏輯流程都能訪問該程序的全部地址空間,所以開銷比多程序低得多;缺點是程式設計複雜度高。
多程序模型
構造併發最簡單的就是使用程序,像fork函式。例如,一個併發伺服器,在父程序中接受客戶端連線請求,然後建立一個新的子程序來為每個新客戶端提供服務。多程序優點:
每個程序互相獨立,不影響主程式的穩定性,子程序崩潰沒關係;
通過增加CPU,就可以容易擴充效能;
可以儘量減少執行緒加鎖/解鎖的影響,極大提高效能,就算是執行緒執行的模組演算法效率低也沒關係;
每個子程序都有2GB地址空間和相關資源,總體能夠達到的效能上限非常大
邏輯控制複雜,需要和主程式互動;
需要跨程序邊界,如果有大資料量傳送,就不太好,適合小資料量傳送、密集運算
多程序排程開銷比較大;
多執行緒模型
每個執行緒都有自己的執行緒上下文,包括一個執行緒ID、棧、棧指標、程式計數器、通用目的暫存器和條件碼。所有的執行在一個程序裡的執行緒共享該程序的整個虛擬地址空間。由於執行緒執行在單一程序中,因此共享這個程序虛擬地址空間的整個內容,包括它的程式碼、資料、堆、共享庫和開啟的檔案。執行緒執行的模型:執行緒和程序的執行模型有些相似,每個程序的宣告週期都是一個執行緒,我們稱之為主執行緒。執行緒是對等的,主執行緒跟其他執行緒的區別就是它先執行。
多執行緒的優點
無需跨程序邊界;
程式邏輯和控制方式簡單;
所有執行緒可以直接共享記憶體和變數等;
執行緒方式消耗的總資源比程序方式好;多執行緒缺點:
每個執行緒與主程式共用地址空間,受限於2GB地址空間;
執行緒之間的同步和加鎖控制比較麻煩;
一個執行緒的崩潰可能影響到整個程式的穩定性;
到達一定的執行緒數程度後,即使再增加CPU也無法提高效能,例如Windows Server 2003,大約是1500個左右的執行緒數就快到極限了(執行緒堆疊設定為1M),如果設定執行緒堆疊為2M,還達不到1500個執行緒總數;
執行緒能夠提高的總效能有限,而且執行緒多了之後,執行緒本身的排程也是一個麻煩事兒,需要消耗較多的CPULinux的執行緒實現是在核外進行的,核內提供的是建立程序的介面do_fork()。核心提供了兩個系統呼叫__clone()和fork(),最終都用不同的引數呼叫do_fork()核內API。 do_fork() 提供了很多引數,包括CLONE_VM(共享記憶體空間)、CLONE_FS(共享檔案系統資訊)、CLONE_FILES(共享檔案描述符表)、CLONE_SIGHAND(共享訊號控制代碼表)和CLONE_PID(共享程序ID,僅對核內程序,即0號程序有效)。當使用fork系統呼叫產生多程序時,核心呼叫do_fork()不使用任何共享屬性,程序擁有獨立的執行環境。當使用pthread_create()來建立執行緒時,則最終設定了所有這些屬性來呼叫__clone(),而這些引數又全部傳給核內的do_fork(),從而建立的”程序”擁有共享的執行環境,只有棧是獨立的,由 __clone()傳入。
即:Linux下不管是多執行緒程式設計還是多程序程式設計,最終都是用do_fork實現的多程序程式設計,只是程序建立時的引數不同,從而導致有不同的共享環境。Linux執行緒在核內是以輕量級程序的形式存在的,擁有獨立的程序表項,而所有的建立、同步、刪除等操作都在核外pthread庫中進行。pthread 庫使用一個管理執行緒(__pthread_manager() ,每個程序獨立且唯一)來管理執行緒的建立和終止,為執行緒分配執行緒ID,傳送執行緒相關的訊號,而主執行緒pthread_create()) 的呼叫者則通過管道將請求資訊傳給管理執行緒。