[TOC]
# 進程 vs. 線程
我們介紹了多進程和多線程,這是實現多任務最常用的兩種方式。現在,我們來討論一下這兩種方式的優缺點。
首先,要實現多任務,通常我們會設計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現在又有多進程+多線程的混合模式,真是把問題越搞越復雜。
# CPU-bound(計算密集型) 和I/O bound(I/O密集型)
我們可以把任務分為計算密集型和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編程模型來實現多任務是一個主要的趨勢。
# 如何優化
網卡、硬盤都由南橋芯片控制,并屬于中、低速設備,所以,在服務器上進行網絡通訊、網絡傳輸、磁盤讀寫均受南橋控制,此類即為IO操作。
IO密集型服務/業務即是以網絡請求壓力大、磁盤讀寫頻繁的操作類型,當進行這些IO密集型操作時,CPU的負載相對較低(現代計算機均集成了對硬件訪問控制的操作邏輯,使得CPU從這些操作中解放出來,提高核心資源的利用率)。
計算密集型,可以理解為在北橋芯片與CPU之間的通訊較高的服務/業務,往往這類操作常見的都是以計算為主的,而計算又是CPU/GPU的專長。
對于服務器,通過開發的服務或是業務,可以在項目之初就根據需求來對資源進行預先估算,大致屬于IO密集型還是計算密集型的業務,并進行項目前期的資源預算等工作的開展,也包括前期的設計和后期的優化。
1. 項目立項過程中,根據需求對應的資源負載類型,提出對服務資源的需求配置
IO密集型的需求,一般來說,如果是磁盤讀寫頻繁,通過對磁盤進行升級,提高磁盤的響應速度和傳輸效率或通過負載技術,將文件讀寫分散到多臺服務器中;如果是網絡請求負載較高,可以通過負載均衡技術,水平擴展服務,提高負載能力;或使用代理緩存服務器,降低核心服務的負載壓力。
計算密集型的需求,首先可以考慮使用計算能力更好的CPU,然后考慮通過消息隊列或其它降維算法,將計算分散的不同的計算結點,進行處理。
2. 項目開發時,進行合理的規劃和業務開發
于IO密集型的需求,在開發過程中,就要考慮盡可能減少IO開銷,**對磁盤讀寫頻繁的業務,可以考慮通過內存緩存將熱數據緩存起來,減少磁盤的請求**。
對于計算密集型的需求,在開發過程中,需要注意計算算法的優化及結果重用,并盡可能進行降維處理,比如通過某種算法將原業務需求的計算分散成可拆分的邏輯,并分散計算進行結果求解,最后進行組合(很像現在大數據處理里的一些模式,可以參考),或通過消息隊列將大量的計算請求分發到其它的計算結點上去。
# 參考
[如保理解IO密集型服務和計算密集型服務](http://ju.outofmemory.cn/entry/214931)