標籤:web 問題 work 應用 RKE 朋友 狀態 進程模型 類型
我們介紹了多進程和多線程,這是實現多任務最常用的兩種方式。現在,我們來討論一下這兩種方式的優缺點。
首先,要實現多任務,通常我們會設計Master-Worker模式,Master負責分配任務,Worker負責執行任務,因此,多任務環境下,通常是一個Master,多個Worker。
如果用多進程實現Master-Worker,主進程就是Master,其他進程就是Worker。
如果用多線程實現Master-Worker,主線程就是Master,其他線程就是Worker。
多進程模式最大的優點就是穩定性高,因為一個子進程崩潰了,不會影響主進程和其他子進程。(當然主進程掛了所有進程就全掛了,但是Master進程只負責分配任務,掛掉的機率低)著名的Apache最早就是採用多進程模式。
多進程模式的缺點是建立進程的代價大,在Unix/Linux系統下,用fork
調用還行,在Windows下建立進程開銷巨大。另外,作業系統能同時啟動並執行進程數也是有限的,在記憶體和CPU的限制下,如果有幾千個進程同時運行,作業系統連調度都會成問題。
多線程模式通常比多進程快一點,但是也快不到哪去,而且,多線程模式致命的缺點就是任何一個線程掛掉都可能直接造成整個進程崩潰,因為所有線程共用進程的記憶體。在Windows上,如果一個線程執行的代碼出了問題,你經常可以看到這樣的提示:“該程式執行了非法操作,即將關閉”,其實往往是某個線程出了問題,但是作業系統會強制結束整個進程。
在Windows下,多線程的效率比多進程要高,所以微軟的IIS伺服器預設採用多線程模式。由於多線程存在穩定性的問題,IIS的穩定性就不如Apache。為了緩解這個問題,IIS和Apache現在又有多進程+多線程的混合模式,真是把問題越搞越複雜。
線程切換
無論是多進程還是多線程,只要數量一多,效率肯定上不去,為什麼呢?
我們打個比方,假設你不幸正在準備中考,每天晚上需要做語文、數學、英語、物理、化學這5科的作業,每項作業耗時1小時。
如果你先花1小時做語文作業,做完了,再花1小時做數學作業,這樣,依次全部做完,一共花5小時,這種方式稱為單任務模型,或者批處理任務模型。
假設你打算切換到多任務模型,可以先做1分鐘語文,再切換到數學作業,做1分鐘,再切換到英語,以此類推,只要切換速度足夠快,這種方式就和單核CPU執行多任務是一樣的了,以幼兒園小朋友的眼光來看,你就正在同時寫5科作業。
但是,切換作業是有代價的,比如從語文切到數學,要先收拾桌子上的語文書本、鋼筆(這叫儲存現場),然後,開啟數學課本、找出圓規直尺(這叫準備新環境),才能開始做數學作業。作業系統在切換進程或者線程時也是一樣的,它需要先儲存當前執行的現場環境(CPU寄存器狀態、記憶體頁等),然後,把新任務的執行環境準備好(恢複上次的寄存器狀態,切換記憶體頁等),才能開始執行。這個切換過程雖然很快,但是也需要耗費時間。如果有幾千個任務同時進行,作業系統可能就主要忙著切換任務,根本沒有多少時間去執行任務了,這種情況最常見的就是硬碟狂響,點視窗無反應,系統處於假死狀態。
所以,多任務一旦多到一個限度,就會消耗掉系統所有的資源,結果效率急劇下降,所有任務都做不好。
計算密集型 vs. IO密集型
是否採用多任務的第二個考慮是任務的類型。我們可以把任務分為計算密集型和IO密集型。
計算密集型任務的特點是要進行大量的計算,消耗CPU資源,比如計算圓周率、對視頻進行高清解碼等等,全靠CPU的運算能力。這種計算密集型任務雖然也可以用多任務完成,但是任務越多,花在任務切換的時間就越多,CPU執行任務的效率就越低,所以,要最高效地利用CPU,計算密集型任務同時進行的數量應當等於CPU的核心數。
計算密集型任務由於主要消耗CPU資源,因此,代碼運行效率至關重要。Python這樣的指令碼語言運行效率很低,完全不適合計算密集型任務。對於計算密集型任務,最好用C語言編寫。
第二種任務的類型是IO密集型,涉及到網路、磁碟IO的任務都是IO密集型任務,這類任務的特點是CPU消耗很少,任務的大部分時間都在等待IO操作完成(因為IO的速度遠遠低於CPU和記憶體的速度)。對於IO密集型任務,任務越多,CPU效率越高,但也有一個限度。常見的大部分任務都是IO密集型任務,比如Web應用。
IO密集型任務執行期間,99%的時間都花在IO上,花在CPU上的時間很少,因此,用運行速度極快的C語言替換用Python這樣運行速度極低的指令碼語言,完全無法提升運行效率。對於IO密集型任務,最合適的語言就是開發效率最高(代碼量最少)的語言,指令碼語言是首選,C語言最差。
非同步IO
考慮到CPU和IO之間巨大的速度差異,一個任務在執行的過程中大部分時間都在等待IO操作,單進程單執行緒模式會導致別的任務無法並存執行,因此,我們才需要多進程模型或者多執行緒模式來支援多任務並發執行。
現代作業系統對IO操作已經做了巨大的改進,最大的特點就是支援非同步IO。如果充分利用作業系統提供的非同步IO支援,就可以用單進程單執行緒模式來執行多任務,這種全新的模型稱為事件驅動模型,Nginx就是支援非同步IO的Web伺服器,它在單核CPU上採用單進程模型就可以高效地支援多任務。在多核CPU上,可以運行多個進程(數量與CPU核心數相同),充分利用多核CPU。由於系統總的進程數量十分有限,因此作業系統調度非常高效。用非同步IO編程模型來實現多任務是一個主要的趨勢。
對應到Python語言,單進程的非同步編程模型稱為協程,有了協程的支援,就可以基於事件驅動編寫高效的多任務程式。我們會在後面討論如何編寫協程。
進程 vs. 線程(python的協程)(轉廖雪峰老師python教程)