多進程還是多線程

摘自:http://wenku.baidu.com/view/e9dd1bd149649b6648d7475c.html
什麼是多線程: 

多線程是爲了使得多個線程並行的工作以完成多項任務,以提高系統的效率。線程是在同一時間需要完成多項任務的時候被實現的。 

使用線程的好處有以下幾點: 

·使用線程可以把佔據長時間的程序中的任務放到後臺去處理 

·用戶界面可以更加吸引人,這樣比如用戶點擊了一個按鈕去觸發某些事件的處理,可以彈出一個進度條來顯示處理的進度 

·程序的運行速度可能加快 

·在一些等待的任務實現上如用戶輸入、文件讀寫和網絡收發數據等,線程就比較游泳了。在這種情況下我們可以釋放一些珍貴的資源如內存佔用等等。 

==== 

■什麼是多進程: 

進 程是程序在計算機上的一次執行活動。當你運行一個程序,你就啓動了一個進程。顯然,程序是死的(靜態的),進程是活的(動態的)。進程可以分爲系統進程和 用戶進程。凡是用於完成操作系統的各種功能的進程就是系統進程,它們就是處於運行狀態下的操作系統本身;用戶進程就不必我多講了吧,所有由你啓動的進程都 是用戶進程。進程是操作系統進行資源分配的單位。 
在Windows下,進程又被細化爲線程,也就是一個進程下有多個能獨立運行的更小的單位。 
在 同一個時間裏,同一個計算機系統中如果允許兩個或兩個以上的進程處於運行狀態,這便是多任務。現代的操作系統幾乎都是多任務操作系統,能夠同時管理多個進 程的運行。 多任務帶來的好處是明顯的,比如你可以邊聽mp3邊上網,與此同時甚至可以將下載的文檔打印出來,而這些任務之間絲毫不會相互干擾。那麼這裏就涉及到並行 的問題,俗話說,一心不能二用,這對計算機也一樣,原則上一個CPU只能分配給一個進程,以便運行這個進程。我們通常使用的計算機中只有一個CPU,也就 是說只有一顆心,要讓它一心多用,同時運行多個進程,就必須使用併發技術。實現併發技術相當複雜,最容易理解的是“時間片輪轉進程調度算法”,它的思想簡 單介紹如下:在操作系統的管理下,所有正在運行的進程輪流使用CPU,每個進程允許佔用CPU的時間非常短(比如10毫秒),這樣用戶根本感覺不出來 CPU是在輪流爲多個進程服務,就好象所有的進程都在不間斷地運行一樣。但實際上在任何一個時間內有且僅有一個進程佔有CPU。 
如果一臺計算機有多個CPU,情況就不同了,如果進程數小於CPU數,則不同的進程可以分配給不同的CPU來運行,這樣,多個進程就是真正同時運行的,這便是並行。但如果進程數大於CPU數,則仍然需要使用併發技術。 
在Windows中,進行CPU分配是以線程爲單位的,一個進程可能由多個線程組成,這時情況更加複雜,但簡單地說,有如下關係: 

總線程數<= CPU數量:並行運行 

總線程數> CPU數量:併發運行 

並行運行的效率顯然高於併發運行,所以在多CPU的計算機中,多任務的效率比較高。但是,如果在多CPU計算機中只運行一個進程(線程),就不能發揮多CPU的優勢。 

這 裏涉及到多任務操作系統的問題,多任務操作系統(如Windows)的基本原理是:操作系統將CPU的時間片分配給多個線程,每個線程在操作系統指定的時 間片內完成(注意,這裏的多個線程是分屬於不同進程的).操作系統不斷的從一個線程的執行切換到另一個線程的執行,如此往復,宏觀上看來,就好像是多個線 程在一起執行.由於這多個線程分屬於不同的進程,因此在我們看來,就好像是多個進程在同時執行,這樣就實現了多任務.
架構設計:多進程還是多線程
就像莎士比亞的“To be, or not to be, that is the question”始終困擾着哈姆雷特,對於“進程還是線程?”這個問題,也經常困擾着那些進行軟件架構設計的傢伙。所以今天打算聊一下我對這個問題的體 會。假如你還搞不清楚線程和進程的區別,請先找本操作系統原理的書好好拜讀一下,再回來看帖。

  由於這個問題很容易引發口水戰,事先聲明如下:多進程和多線程,無法一概而論地說誰比誰好。因此本帖主要描述特定場景(與我所負責的產品相關)下,進程和線程的權衡經驗,僅供大夥兒參考。

  由於特定場景是本帖討論的前提,先說說我目前負責的產品的特點:業務邏輯比較複雜、業務數據量比較大、對數據實時處理的性能要求比較高、對健壯性和安全性要求比較高、要求跨平臺(包括操作系統、數據庫)、某些情況下需要分佈部署。

  上面說了一大堆,其實有不少的應用系統符合上述特點,比如:某些網絡遊戲服務器、某些金融行業的業務系統、某些電子商務的交易系統等等。如果你正在從事的是類似的應用系統的設計,希望我下面介紹的經驗對你有幫助。

   進程顆粒度問題

  大夥兒應該明白,進程和線程都是處理併發(concurrency)的手段。對於上述這種比較複雜的系統,如果你企圖全部用進程(見注1)或者 全部用線程(見注2)來處理併發,估計會死得很難看。所以,關鍵問題就是如何在進程和線程之間進行平衡(也就是確定進程顆粒度的問題)。

  我個人建議,儘量以業務邏輯的單元來劃分進程。這樣做的好處有如下幾點:

  1、避免扯皮

  一般來說,某個固定業務邏輯的開發人員也是相對固定的。如果業務邏輯對應的某個進程崩潰了,測試人員容易快速定位肇事者,然後直接提交Bug給他/她。

  反之,一個進程搞得太龐大,N多人摻和在裏面,一旦進程崩潰了,相關編程人員之間很容易互相扯皮,不利於維護安定團結的局面;另外,由於測試人員經常搞不清楚Bug屬於誰,經常給錯Bug,也容易製造人民內部矛盾。

  從上面可以看出來,相對細的進程顆粒度能夠避免一些管理上的麻煩。由於XXX經常教導我們:“穩定壓倒一切”,所以該優點列第一條。

  2、健壯性、容錯性

  一般來說,開發人員的水平參差不齊,優秀的畢竟是少數(具體參見“二八原理系列”的帖子)。所以難免會有菜鳥程序員搞出低級錯誤,而有些低級錯誤是致命的,會導致進程的崩潰。

  如果你是以業務邏輯劃分進程,一個業務邏輯的進程崩潰,對其它業務邏輯的影響不大(除非是該業務邏輯的依賴方);因此就不會出現“注2”提到的問題。

  3、分佈式

  我常碰見的分佈式部署需求,一般都是按照業務邏輯的維度來劃分。比如系統中有一個認證模塊,裏面包含有敏感的用戶認證信息。這時候客戶就會要求把該模塊單獨部署在一臺經過安全加固的主機中(以防階級敵人搞破壞)。

  如果是以業務邏輯爲單位劃分進程,要滿足上述的部署需求就相對容易了(只要再配合恰當的進程間通訊機制,下面會提到)。

  另外,支持分佈式部署還可以順帶解決性能問題。比如某個業務邏輯模塊特別消耗硬件資源(比如內存、CPU、硬盤、帶寬),就可以把它拿出去單獨放一臺機器上跑。

  4、跨編程語言

  這個好處可能很多人容易忽略。一般來說,每個編程語言都有各自的優缺點。如果你通過業務邏輯劃分進程,就可以根據不同的業務邏輯的特點來選擇合適的編程語言。

  比如:對於性能敏感的模塊,我就使用C++搞定;而對於一些業務邏輯密集型的模塊,則使用Java或Python開發。

   進程間通訊(以下簡稱IPC)問題

  既然不可能把整個系統放入一個進程,那就必然會碰到IPC的問題。下面就來說一下該如何選擇IPC。

  各種操作系統裏面,有很多稀奇古怪的IPC類型。由於要考慮跨平臺,首先砍掉一批(關於IPC的跨平臺問題,我在“跨平臺開發”系列中會提 到)。剩下的IPC類型中,能夠進行數據傳輸的IPC就不多了,主要有如下幾種:套接字(以下簡稱Socket)、共享內存、管道、文件。

  其中Socket是我強烈推薦的IPC方式,理由如下:使用Socket可以天然地支持分佈式部署;使用Socket可以比較容易地實現多種編 程語言的混合(比如C++、Java、Python、Flex都支持Socket);使用Socket還可以省掉了一大坨“鎖操作”的代碼。

  列位看官中,或許有人在擔心Socket的性能問題,其實大可不必多慮。當兩個進程在本機上進行Socket通訊時,由於可以使用 localhost環回地址,數據不用經過物理網卡,操作系統內核還可以進行某些優化。這種情況下,Socket相對其它幾種IPC機制,不會有太大的性 能偏差。

  最後再補充一下,Socket方式也可以有效防止扯皮問題。舉個例子:張三寫了一個進程A,李四寫了一個進程B,進程A通過Socket方式發 數據給進程B。突然有一天,兩個進程的通訊出故障了。然後張三就說是李四接收數據出錯;李四就說張三發送數據出錯。這時候怎麼辦捏?很簡單,隨便找個 Sniffer軟件當場抓一下數據包並Dump出來看,問題就水落石出了。

   爲啥還要線程?

  上面說了這麼多進程的好處,有同學要問了:“那線程有什麼用捏?”總的來說,使用線程出於兩方面的考慮:性能因素和編碼方便。

  1、性能因素

  由於某些操作系統(比如Windows)中的進程比較重型,如果頻繁創建進程或者創建大量進程,會導致操作系統的負載過高。舉例如下:

  假設你要開發一個類似Web Server的應用。你針對每一個客戶端請求創建一個對應的進程用於進行數據交互(是不是想起了古老的CGI :-)。一旦這個系統擴容,用戶的併發連接數一增加,你的應用立馬死翹翹。

  上面的例子表明,跨平臺軟件系統的進程數要保持相對穩定。如果你的進程數會隨着某些環境因素呈線性增長,那就相當不妙了(順帶說一下,如果線程數會隨着環境因素呈線性增長,也相當不妙)。而根據業務邏輯的單元劃分進程,順便能達到“進程數的相對穩定”的效果。

  2、編碼方面

  由於業務邏輯內部的數據耦合比較緊密。如果業務邏輯內部的併發也用進程來實現,可能會導致大量的IPC編碼(任意兩個進程之間只要有數據交互,就得寫一坨IPC代碼)。這或許會讓相關的編程人員怨聲載道。

  當然,編碼方面的問題也不是絕對的。假如你的系統有很成熟且方便易用的IPC庫,可以比較透明地封裝IPC相關操作,那這方面的問題也就不存在了。

魚還是熊掌:淺談多進程多線程的選擇

關於多進程和多線程,教科書上最經典的一句話是“進程是資源分配的最小單位,線程是CPU調度的最小單位”,這句話應付考試基本上夠了,但如果在工作中遇到類似的選擇問題,那就沒有這麼簡單了,選的不好,會讓你深受其害。
經常在網絡上看到有的XDJM問“多進程好還是多線程好?”、“Linux下用多進程還是多線程?”等等期望一勞永逸的問題,我只能說:沒有最好,只有更好。根據實際情況來判斷,哪個更加合適就是哪個好。
我們按照多個不同的維度,來看看多線程和多進程的對比(注:因爲是感性的比較,因此都是相對的,不是說一個好得不得了,另外一個差的無法忍受)。

看起來比較簡單,優勢對比上是“線程 3.5 v 2.5 進程”,我們只管選線程就是了?
呵呵,有這麼簡單我就不用在這裏浪費口舌了,還是那句話,沒有絕對的好與壞,只有哪個更加合適的問題。我們來看實際應用中究竟如何判斷更加合適。
1)需要頻繁創建銷燬的優先用線程
原因請看上面的對比。
這種原則最常見的應用就是Web服務器了,來一個連接建立一個線程,斷了就銷燬線程,要是用進程,創建和銷燬的代價是很難承受的
2)需要進行大量計算的優先使用線程
所謂大量計算,當然就是要耗費很多CPU,切換頻繁了,這種情況下線程是最合適的。
這種原則最常見的是圖像處理、算法處理。
3)強相關的處理用線程,弱相關的處理用進程
什麼叫強相關、弱相關?理論上很難定義,給個簡單的例子就明白了。
一 般的Server需要完成如下任務:消息收發、消息處理。“消息收發”和“消息處理”就是弱相關的任務,而“消息處理”裏面可能又分爲“消息解碼”、“業 務處理”,這兩個任務相對來說相關性就要強多了。因此“消息收發”和“消息處理”可以分進程設計,“消息解碼”、“業務處理”可以分線程設計。
當然這種劃分方式不是一成不變的,也可以根據實際情況進行調整。
4)可能要擴展到多機分佈的用進程,多核分佈的用線程
原因請看上面對比。
5)都滿足需求的情況下,用你最熟悉、最拿手的方式
至於“數據共享、同步”、“編程、調試”、“可靠性”這幾個維度的所謂的“複雜、簡單”應該怎麼取捨,我只能說:沒有明確的選擇方法。但我可以告訴你一個選擇原則:如果多進程和多線程都能夠滿足要求,那麼選擇你最熟悉、最拿手的那個。
需要提醒的是:雖然我給了這麼多的選擇原則,但實際應用中基本上都是“進程+線程”的結合方式,千萬不要真的陷入一種非此即彼的誤區。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章