在看thinking in java 的 並發 章節時,里面有這樣一句話:實現並發的最簡單的方式就是多進程,但沒有進一步的解釋說明,然后后面都在講多線程編程,當時就有疑惑,那到底多進程和多線程該如何選擇,
網上找答案時,發現這篇文章總結的比較到位,遂轉來分享學習。完整的原文包括一些簡單的數據測試,具體可以看原博客。
-----------------------------------------------------------------------------------
轉自:http://blog.csdn.net/lishenglong666/article/details/8557215
魚還是熊掌:淺談多進程多線程的選擇
關於多進程和多線程,教科書上最經典的一句話是“進程是資源分配的最小單位,線程是CPU調度的最小單位”,這句話應付考試基本上夠了,但如果在工作中遇到類似的選擇問題,那就沒有這么簡單了,選的不好,會讓你深受其害。
經常在網絡上看到有的XDJM問“多進程好還是多線程好?”、“Linux下用多進程還是多線程?”等等期望一勞永逸的問題,我只能說:沒有最好,只有更好。根據實際情況來判斷,哪個更加合適就是哪個好。
我們按照多個不同的維度,來看看多線程和多進程的對比(注:因為是感性的比較,因此都是相對的,不是說一個好得不得了,另外一個差的無法忍受)。
對比維度 |
多進程 |
多線程 |
總結 |
數據共享、同步 |
數據共享復雜,需要用IPC;數據是分開的,同步簡單 |
因為共享進程數據,數據共享簡單,但也是因為這個原因導致同步復雜 |
各有優勢 |
內存、CPU |
占用內存多,切換復雜,CPU利用率低 |
占用內存少,切換簡單,CPU利用率高 |
線程占優 |
創建銷毀、切換 |
創建銷毀、切換復雜,速度慢 |
創建銷毀、切換簡單,速度很快 |
線程占優 |
編程、調試 |
編程簡單,調試簡單 |
編程復雜,調試復雜 |
進程占優 |
可靠性 |
進程間不會互相影響 |
一個線程掛掉將導致整個進程掛掉 |
進程占優 |
分布式 |
適應於多核、多機分布式;如果一台機器不夠,擴展到多台機器比較簡單 |
適應於多核分布式 |
進程占優 |
看起來比較簡單,優勢對比上是“線程 3.5 v 2.5 進程”,我們只管選線程就是了?
呵呵,有這么簡單我就不用在這里浪費口舌了,還是那句話,沒有絕對的好與壞,只有哪個更加合適的問題。我們來看實際應用中究竟如何判斷更加合適。
1)需要頻繁創建銷毀的優先用線程
原因請看上面的對比。
這種原則最常見的應用就是Web服務器了,來一個連接建立一個線程,斷了就銷毀線程,要是用進程,創建和銷毀的代價是很難承受的
2)需要進行大量計算的優先使用線程
所謂大量計算,當然就是要耗費很多CPU,切換頻繁了,這種情況下線程是最合適的。
這種原則最常見的是圖像處理、算法處理。
3)強相關的處理用線程,弱相關的處理用進程
什么叫強相關、弱相關?理論上很難定義,給個簡單的例子就明白了。
一般的Server需要完成如下任務:消息收發、消息處理。“消息收發”和“消息處理”就是弱相關的任務,而“消息處理”里面可能又分為“消息解碼”、“業務處理”,這兩個任務相對來說相關性就要強多了。因此“消息收發”和“消息處理”可以分進程設計,“消息解碼”、“業務處理”可以分線程設計。
當然這種划分方式不是一成不變的,也可以根據實際情況進行調整。
4)可能要擴展到多機分布的用進程,多核分布的用線程
原因請看上面對比。
5)都滿足需求的情況下,用你最熟悉、最拿手的方式
至於“數據共享、同步”、“編程、調試”、“可靠性”這幾個維度的所謂的“復雜、簡單”應該怎么取舍,我只能說:沒有明確的選擇方法。但我可以告訴你一個選擇原則:如果多進程和多線程都能夠滿足要求,那么選擇你最熟悉、最拿手的那個。
需要提醒的是:雖然我給了這么多的選擇原則,但實際應用中基本上都是“進程+線程”的結合方式,千萬不要真的陷入一種非此即彼的誤區。
1、進程與線程
進程是程序執行時的一個實例,即它是程序已經執行到課中程度的數據結構的匯集。從內核的觀點看,進程的目的就是擔當分配系統資源(CPU時間、內存等)的基本單位。
線程是進程的一個執行流,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。一個進程由幾個線程組成(擁有很多相對獨立的執行流的用戶程序共享應用程序的大部分數據結構),線程與同屬一個進程的其他的線程共享進程所擁有的全部資源。
"進程——資源分配的最小單位,線程——程序執行的最小單位"
進程有獨立的地址空間,一個進程崩潰后,在保護模式下不會對其它進程產生影響,而線程只是一個進程中的不同執行路徑。線程有自己的堆棧和局部變量,但線程沒有單獨的地址空間,一個線程死掉就等於整個進程死掉,所以多進程的程序要比多線程的程序健壯,但在進程切換時,耗費資源較大,效率要差一些。但對於一些要求同時進行並且又要共享某些變量的並發操作,只能用線程,不能用進程。
總的來說就是:進程有獨立的地址空間,線程沒有單獨的地址空間(同一進程內的線程共享進程的地址空間)。(下面的內容摘自Linux下的多線程編程)
使用多線程的理由之一是和進程相比,它是一種非常"節儉"的多任務操作方式。我們知道,在Linux系統下,啟動一個新的進程必須分配給它獨立的地址空間,建立眾多的數據表來維護它的代碼段、堆棧段和數據段,這是一種"昂貴"的多任務工作方式。而運行於一個進程中的多個線程,它們彼此之間使用相同的地址空間,共享大部分數據,啟動一個線程所花費的空間遠遠小於啟動一個進程所花費的空間,而且,線程間彼此切換所需的時間也遠遠小於進程間切換所需要的時間。據統計,總的說來,一個進程的開銷大約是一個線程開銷的30倍左右,當然,在具體的系統上,這個數據可能會有較大的區別。
使用多線程的理由之二是線程間方便的通信機制。對不同進程來說,它們具有獨立的數據空間,要進行數據的傳遞只能通過通信的方式進行,這種方式不僅費時,而且很不方便。線程則不然,由於同一進程下的線程之間共享數據空間,所以一個線程的數據可以直接為其它線程所用,這不僅快捷,而且方便。當然,數據的共享也帶來其他一些問題,有的變量不能同時被兩個線程所修改,有的子程序中聲明為static的數據更有可能給多線程程序帶來災難性的打擊,這些正是編寫多線程程序時最需要注意的地方。
除了以上所說的優點外,不和進程比較,多線程程序作為一種多任務、並發的工作方式,當然有以下的優點:
- 提高應用程序響應。這對圖形界面的程序尤其有意義,當一個操作耗時很長時,整個系統都會等待這個操作,此時程序不會響應鍵盤、鼠標、菜單的操作,而使用多線程技術,將耗時長的操作(time consuming)置於一個新的線程,可以避免這種尷尬的情況。
- 使多CPU系統更加有效。操作系統會保證當線程數不大於CPU數目時,不同的線程運行於不同的CPU上。
- 改善程序結構。一個既長又復雜的進程可以考慮分為多個線程,成為幾個獨立或半獨立的運行部分,這樣的程序會利於理解和修改。
在Unix上編程采用多線程還是多進程的爭執由來已久,這種爭執最常見到在B/S通訊中服務端並發技術 的選型上,比如WEB服務器技術中,Apache是采用多進程的(perfork模式,每客戶連接對應一個進程,每進程中只存在唯一一個執行線 程),Java的Web容器Tomcat、Websphere等都是多線程的(每客戶連接對應一個線程,所有線程都在一個進程中)。
從Unix發展歷史看,伴隨着Unix的誕生多進程就出現了,而多線程很晚才被系統支持,例如Linux直到內核2.6,才支持符合Posix規范的NPTL線程庫。進程和線程的特點,也就是各自的優缺點如下:
進程優點:編程、調試簡單,可靠性較高。
進程缺點:創建、銷毀、切換速度慢,內存、資源占用大。
線程優點:創建、銷毀、切換速度快,內存、資源占用小。
線程缺點:編程、調試復雜,可靠性較差。
上面的對比可以歸結為一句話:“線程快而進程可靠性高”。線程有個別名叫“輕量級進程”,在有的書籍資料上介紹線程可以十倍、百倍的效率快於進程; 而進程之間不共享數據,沒有鎖問題,結構簡單,一個進程崩潰不像線程那樣影響全局,因此比較可靠。我相信這個觀點可以被大部分人所接受,因為和我們所接受的知識概念是相符的。
在寫這篇文章前,我也屬於這“大部分人”,這兩年在用C語言編寫的幾個C/S通訊程序中,因時間緊總是采用多進程並發技術,而且是比較簡單的現場為 每客戶fork()一個進程,當時總是擔心並發量增大時負荷能否承受,盤算着等時間充裕了將它改為多線程形式,或者改為預先創建進程的形式,直到最近在網 上看到了一篇論文《Linux系統下多線程與多進程性能分析》作者“周麗 焦程波 蘭巨龍”,才認真思考這個問題,我自己也做了實驗,結論和論文作者的相似,但對大部分人可以說是顛覆性的。