IO復用、多進程和多線程三種並發編程模型
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個線程總數;
線程能夠提高的總性能有限,而且線程多了之后,線程本身的調度也是一個麻煩事兒,需要消耗較多的CPU
Linux的線程實現是在核外進行的,核內提供的是創建進程的接口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()) 的調用者則通過管道將請求信息傳給管理線程。
即: Linux下不管是多線程編程還是多進程編程,最終都是用do_fork實現的多進程編程,只是進程創建時的參數不同,從而導致有不同的共享環境。Linux線程在核內是以輕量級進程的形式存在的,擁有獨立的進程表項,而所有的創建、同步、刪除等操作都在核外pthread庫中進行。pthread 庫使用一個管理線程(__pthread_manager() ,每個進程獨立且唯一)來管理線程的創建和終止,為線程分配線程ID,發送線程相關的信號,而主線程pthread_create()) 的調用者則通過管道將請求信息傳給管理線程。