多程序和多執行緒學習
關於多程序和多執行緒,教科書上最經典的一句話是“程序是資源分配的最小單位,執行緒是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執行緒庫。程序和執行緒的特點,也就是各自的優缺點如下:
程序優點:程式設計、除錯簡單,可靠性較高。
程序缺點:建立、銷燬、切換速度慢,記憶體、資源佔用大。
執行緒優點:建立、銷燬、切換速度快,記憶體、資源佔用小。
執行緒缺點:程式設計、除錯複雜,可靠性較差。
上面的對比可以歸結為一句話:“執行緒快而程序可靠性高”。執行緒有個別名叫“輕量級程序”,在有的書籍資料上介紹執行緒可以十倍、百倍的效率快於程序; 而程序之間不共享資料,沒有鎖問題,結構簡單,一個程序崩潰不像執行緒那樣影響全域性,因此比較可靠。我相信這個觀點可以被大部分人所接受,因為和我們所接受的知識概念是相符的。