http://www.cnblogs.com/lmule/archive/2010/08/18/1802774.html
簡而言之,一個程序至少有一個進程,一個進程至少有一個線程.
線程的劃分尺度小于進程万栅,使得多線程程序的并發(fā)性高辜窑。
另外,進程在執(zhí)行過程中擁有獨立的內(nèi)存單元,而多個線程共享內(nèi)存孽尽,從而極大地提高了程序的運行效率雷厂。
線程在執(zhí)行過程中與進程還是有區(qū)別的滞欠。每個獨立的線程有一個程序運行的入口梨撞、順序執(zhí)行序列和程序的出口。但是線程不能夠獨立執(zhí)行甜熔,必須依存在應(yīng)用程序中圆恤,由應(yīng)用程序提供多個線程執(zhí)行控制。
從邏輯角度來看,多線程的意義在于一個應(yīng)用程序中盆昙,有多個執(zhí)行部分可以同時執(zhí)行羽历。但操作系統(tǒng)并沒有將多個線程看做多個獨立的應(yīng)用,來實現(xiàn)進程的調(diào)度和管理以及資源分配淡喜。這就是進程和線程的重要區(qū)別秕磷。
進程是具有一定獨立功能的程序關(guān)于某個數(shù)據(jù)集合上的一次運行活動,進程是系統(tǒng)進行資源分配和調(diào)度的一個獨立單位.
線程是進程的一個實體,是CPU調(diào)度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程自己基本上不擁有系統(tǒng)資源,只擁有一點在運行中必不可少的資源(如程序計數(shù)器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源.
一個線程可以創(chuàng)建和撤銷另一個線程;同一個進程中的多個線程之間可以并發(fā)執(zhí)行.
進程和線程的主要差別在于它們是不同的操作系統(tǒng)資源管理方式。進程有獨立的地址空間炼团,一個進程崩潰后澎嚣,在保護模式下不會對其它進程產(chǎn)生影響,而線程只是一個進程中的不同執(zhí)行路徑瘟芝。線程有自己的堆棧和局部變量易桃,但線程之間沒有單獨的地址空間,一個線程死掉就等于整個進程死掉锌俱,所以多進程的程序要比多線程的程序健壯晤郑,但在進程切換時,耗費資源較大贸宏,效率要差一些造寝。但對于一些要求同時進行并且又要共享某些變量的并發(fā)操作,只能用線程吭练,不能用進程诫龙。如果有興趣深入的話,我建議你們看看《現(xiàn)代操作系統(tǒng)》或者《操作系統(tǒng)的設(shè)計與實現(xiàn)》鲫咽。對就個問題說得比較清楚签赃。
5.1 簡介
進程(process)是一塊包含了某些資源的內(nèi)存區(qū)域。操作系統(tǒng)利用進程把它的工作劃分為一些功能單元浑侥。
進程中所包含的一個或多個執(zhí)行單元稱為線程(thread)姊舵。進程還擁有一個私有的虛擬地址空間晰绎,該空間僅能被它所包含的線程訪問寓落。
當(dāng)運行.NET程序時,進程還會把被稱為CLR的軟件層包含到它的內(nèi)存空間中荞下。上一章曾經(jīng)對CLR做了詳細描述伶选。該軟件層是在進程創(chuàng)建期間由運行時宿主載入的(參見4.2.3節(jié))。
線程只能歸屬于一個進程并且它只能訪問該進程所擁有的資源尖昏。當(dāng)操作系統(tǒng)創(chuàng)建一個進程后仰税,該進程會自動申請一個名為主線程或首要線程的線程。主線程將執(zhí)行運行時宿主, 而運行時宿主會負責(zé)載入CLR抽诉。
應(yīng)用程序(application)是由一個或多個相互協(xié)作的進程組成的陨簇。例如,Visual Studio開發(fā)環(huán)境就是利用一個進程編輯源文件迹淌,并利用另一個進程完成編譯工作的應(yīng)用程序河绽。
在Windows NT/2000/XP操作系統(tǒng)下己单,我們可以通過任務(wù)管理器在任意時間查看所有的應(yīng)用程序和進程。盡管只打開了幾個應(yīng)用程序耙饰,但是通常情況下將有大約30個進程同時運行纹笼。 事實上,為了管理當(dāng)前的會話和任務(wù)欄以及其他一些任務(wù)苟跪,系統(tǒng)執(zhí)行了大量的進程廷痘。
5.2 進程
5.2.1 簡介
在運行于32位處理器上的32位Windows操作系統(tǒng)中,可將一個進程視為一段大小為4GB(232字節(jié))的線性內(nèi)存空間件已,它起始于0x00000000結(jié)束于0xFFFFFFFF笋额。這段內(nèi)存空間不能被其他進程所訪問,所以稱為該進程的私有空間篷扩。這段空間被平分為兩塊鳞陨,2GB被系統(tǒng)所有,剩下2GB被用戶所有瞻惋。
如果有N個進程運行在同一臺機器上厦滤,那么將需要N×4GB的海量RAM,還好事實并非如此歼狼。
Windows是按需為每個進程分配內(nèi)存的掏导,4GB是32位系統(tǒng)中一個進程所占空間的上限。
將進程所需的內(nèi)存劃分為4KB大小的內(nèi)存頁羽峰,并根據(jù)使用情況將這些內(nèi)存頁存儲在硬盤上或加載到RAM中趟咆,通過系統(tǒng)的這種虛擬內(nèi)存機制,我們可以有效地減少對實際內(nèi)存的需求量梅屉。當(dāng)然這些對用戶和開發(fā)者來說都是透明的值纱。
5.2.2 System.Diagnostics.Process類
System.Diagnostics.Process類的實例可以引用一個進程,被引用的進程包含以下幾種坯汤。
該實例的當(dāng)前進程虐唠。
本機上除了當(dāng)前進程的其他進程。
遠程機器上的某個進程惰聂。
通過該類所包含的方法和字段疆偿,可以創(chuàng)建或銷毀一個進程,并且可以獲得一個進程的相關(guān)信息搓幌。下面將討論一些使用該類實現(xiàn)的常見任務(wù)杆故。
5.2.3 創(chuàng)建和銷毀子進程
下面的程序創(chuàng)建了一個稱為子進程的新進程。在這種情況下溉愁,初始的進程稱為父進程处铛。子進程啟動了一個記事本應(yīng)用程序。父進程的線程在等待1秒后銷毀該子進程。該程序的執(zhí)行效果就是打開并關(guān)閉記事本撤蟆。
例5-1
靜態(tài)方法Start()可以使用已存在的Windows文件擴展名關(guān)聯(lián)機制篙贸。例如,我們可以利用下面的代碼執(zhí)行同樣的操作枫疆。
默認(rèn)情況下爵川,子進程將繼承其父進程的安全上下文。但還可以使用Process.Start()方法的一個重載版本在任意用戶的安全上下文中啟動該子進程息楔,當(dāng)然需要通過一個System.Diagnostics. ProcessStartInfo類的實例來提供該用戶的用戶名和密碼寝贡。
5.2.4 避免在一臺機器上同時運行同一應(yīng)用程序的多個實例
有些應(yīng)用程序需要這種功能。實際上值依,通常來說在同一臺機器上同時運行一個應(yīng)用程序的多個實例并沒有意義圃泡。
直到現(xiàn)在,為了在Windows下滿足上述約束愿险,開發(fā)者最常用的方法仍然是使用有名互斥體(named mutex)技術(shù)(參見5.7.2節(jié))颇蜡。然而采用這種技術(shù)來滿足上述約束存在以下缺點:
該技術(shù)具有使互斥體的名字被其他應(yīng)用程序所使用的較小的、潛在的風(fēng)險辆亏。在這種情況下該技術(shù)將不再有效并且會造成很難檢測到的bug风秤。
該技術(shù)不能解決我們僅允許一個應(yīng)用程序產(chǎn)生N個實例這種一般的問題。
幸而在System.Diagnostics.Process類中擁有GetCurrentProcess()(返回當(dāng)前進程)和GetPro- cesses()(返回機器上所有的進程)這樣的靜態(tài)方法扮叨。在下面的程序中我們?yōu)樯鲜鰡栴}找到了一個優(yōu)雅且簡單的解決方案缤弦。
例5-2
通過方法參數(shù)指定了遠程機器的名字后,GetProcesses()方法也可以返回遠程機器上所有的進程彻磁。
5.2.5 終止當(dāng)前進程
可以調(diào)用System.Environment類中的靜態(tài)方法Exit(int exitCode)或FailFast(stringmessage)終止當(dāng)前進程碍沐。Exit()方法是最好的選擇,它將徹底終止進程并向操作系統(tǒng)返回指定的退出代碼值衷蜓。之所以稱為徹底終止是因為當(dāng)前對象的所有清理工作以及finally塊的執(zhí)行都將由不同的線程完成累提。當(dāng)然,終止進程將花費一定的時間磁浇。
顧名思義斋陪,F(xiàn)ailFast()方法可以迅速終止進程。Exit()方法所做的預(yù)防措施將被它忽略扯夭。只有一個包含了指定信息的嚴(yán)重錯誤會被操作系統(tǒng)記錄到日志中鳍贾。你可能想要在探查問題的時候使用該方法,因為可以將該程序的徹底終止視為數(shù)據(jù)惡化的起因交洗。
5.3 線程
5.3.1 簡介
一個線程包含以下內(nèi)容。
一個指向當(dāng)前被執(zhí)行指令的指令指針橡淑;
一個棧构拳;
一個寄存器值的集合,定義了一部分描述正在執(zhí)行線程的處理器狀態(tài)的值;
一個私有的數(shù)據(jù)區(qū)置森。
所有這些元素都歸于線程執(zhí)行上下文的名下斗埂。處在同一個進程中的所有線程都可以訪問該進程所包含的地址空間,當(dāng)然也包含存儲在該空間中的所有資源凫海。
我們不準(zhǔn)備討論線程在內(nèi)核模式或者用戶模式執(zhí)行的問題呛凶。盡管.NET以前的Windows一直使用這兩種模式,并且依然存在行贪,但是對.NET Framework來說它們是不可見的漾稀。
并行使用一些線程通常是我們在實現(xiàn)算法時的自然反應(yīng)。實際上建瘫,一個算法往往由一系列可以并發(fā)執(zhí)行的任務(wù)組成。但是需要引起注意的是,使用大量的線程將引起過多的上下文切換日戈,最終反而影響了性能康吵。
同樣,幾年前我們就注意到橄浓,預(yù)測每18個月處理器運算速度增加一倍的摩爾定律已不再成立粒梦。處理器的頻率停滯在3GHz~4GHz上下。這是由于物理上的限制荸实,需要一段時間才能取得突破谍倦。同時,為了在性能競爭中不會落敗泪勒,較大的處理器制造商如AMD和Intel目前都將目標(biāo)轉(zhuǎn)向多核芯片昼蛀。因此我們可以預(yù)計在接下去的幾年中這種類型的架構(gòu)將廣泛被采用。在這種情況下圆存,改進應(yīng)用性能的唯一方案就是合理地利用多線程技術(shù)叼旋。
5.3.2 受托管的線程與 Windows線程
必須要了解,執(zhí)行.NET應(yīng)用的線程實際上仍然是Windows線程沦辙。但是夫植,當(dāng)某個線程被CLR所知時,我們將它稱為受托管的線程油讯。具體來說详民,由受托管的代碼創(chuàng)建出來的線程就是受托管的線程。如果一個線程由非托管的代碼所創(chuàng)建陌兑,那么它就是非托管的線程沈跨。不過,一旦該線程執(zhí)行了受托管的代碼它就變成了受托管的線程兔综。
一個受托管的線程和非托管的線程的區(qū)別在于饿凛,CLR將創(chuàng)建一個System.Threading.Thread類的實例來代表并操作前者狞玛。在內(nèi)部實現(xiàn)中,CLR將一個包含了所有受托管線程的列表保存在一個叫做ThreadStore地方涧窒。
CLR確保每一個受托管的線程在任意時刻都在一個AppDomain中執(zhí)行心肪,但是這并不代表一個線程將永遠處在一個AppDomain中,它可以隨著時間的推移轉(zhuǎn)到其他的AppDomain中纠吴。關(guān)于AppDomain的概念參見4.1硬鞍。
從安全的角度來看,一個受托管的線程的主用戶與底層的非托管線程中的Windows主用戶是無關(guān)的戴已。
5.3.3 搶占式多任務(wù)處理
我們可以問自己下面這個問題: 我的計算機只有一個處理器固该,然而在任務(wù)管理器中我們卻可以看到數(shù)以百計的線程正同時運行在機器上!這怎么可能呢恭陡?
多虧了搶占式多任務(wù)處理蹬音,通過它對線程的調(diào)度,使得上述問題成為可能休玩。調(diào)度器作為Windows內(nèi)核的一部分著淆,將時間切片,分成一段段的時間片拴疤。這些時間間隔以毫秒為精度且長度并不固定永部。針對每個處理器,每個時間片僅服務(wù)于單獨一個線程呐矾。線程的迅速執(zhí)行給我們造成了它們在同時運行的假象苔埋。我們在兩個時間片的間隔中進行上下文切換。該方法的優(yōu)點在于蜒犯,那些正在等待某些Windows資源的線程將不會浪費時間片组橄,直到資源有效為止。
之所以用搶占式這個形容詞來修飾這種多任務(wù)管理方式罚随,是因為在此種方式下線程將被系統(tǒng)強制性中斷玉工。那些對此比較好奇的人應(yīng)該了解到,在上下文切換的過程中淘菩,操作系統(tǒng)會在下一個線程將要執(zhí)行的代碼中插入一條跳轉(zhuǎn)到下一個上下文切換的指令遵班。該指令是一個軟中斷,如果線程在遇到這條指令前就終止了(例如潮改,它正在等待某個資源)狭郑,那么該指定將被刪除而上下文切換也將提前發(fā)生。
搶占式多任務(wù)處理的主要缺點在于汇在,必須使用一種同步機制來保護資源以避免它們被無序訪問翰萨。除此之外,還有另一種多任務(wù)管理模型趾疚,被稱為協(xié)調(diào)式多任務(wù)管理缨历,其中線程間的切換將由線程自己負責(zé)完成以蕴。該模型普遍認(rèn)為太過危險糙麦,原因在于線程間的切換不發(fā)生的風(fēng)險太大辛孵。如我們在4.2.8節(jié)中所解釋的那樣,該機制會在內(nèi)部使用以提升某些服務(wù)器的性能赡磅,例如SQL Server2005魄缚。但Windows操作系統(tǒng)僅僅實現(xiàn)了搶占式多任務(wù)處理。
?