不會(huì)吧!不會(huì)吧!還有人搞不懂進(jìn)程和線程!
進(jìn)程
我們都知道計(jì)算機(jī)的核心是CPU,它承擔(dān)了所有的計(jì)算任務(wù),而操作系統(tǒng)是計(jì)算機(jī)的管理者,它負(fù)責(zé)任務(wù)的調(diào)度,資源的分配和管理,
統(tǒng)領(lǐng)整個(gè)計(jì)算機(jī)硬件;應(yīng)用程序是具有某種功能的程序,程序是運(yùn)行于操作系統(tǒng)之上的。
進(jìn)程是一個(gè)具有一定獨(dú)立功能的程序在一個(gè)數(shù)據(jù)集上的一次動(dòng)態(tài)執(zhí)行的過(guò)程,是操作系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位,是應(yīng)用
程序運(yùn)行的載體。進(jìn)程是一種抽象的概念,從來(lái)沒(méi)有統(tǒng)一的標(biāo)準(zhǔn)定義。進(jìn)程一般由程序,數(shù)據(jù)集合和進(jìn)程控制塊三部分組成。程序用于描述
進(jìn)程要完成的功能,是控制進(jìn)程執(zhí)行的指令集;數(shù)據(jù)集合是程序在執(zhí)行時(shí)所需要的數(shù)據(jù)和工作區(qū);程序控制塊包含進(jìn)程的描述信息和控制信息
是進(jìn)程存在的唯一標(biāo)志
進(jìn)程具有的特征:
動(dòng)態(tài)性:進(jìn)程是程序的一次執(zhí)行過(guò)程,是臨時(shí)的,有生命期的,是動(dòng)態(tài)產(chǎn)生,動(dòng)態(tài)消亡的;
并發(fā)性:任何進(jìn)程都可以同其他進(jìn)行一起并發(fā)執(zhí)行;
獨(dú)立性:進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位;
結(jié)構(gòu)性:進(jìn)程由程序,數(shù)據(jù)和進(jìn)程控制塊三部分組成
避免在一臺(tái)機(jī)器上同時(shí)運(yùn)行同一應(yīng)用程序的多個(gè)實(shí)例
有些應(yīng)用程序需要這種功能。實(shí)際上,通常來(lái)說(shuō)在同一臺(tái)機(jī)器上同時(shí)運(yùn)行一個(gè)應(yīng)用程序的多個(gè)實(shí)例并沒(méi)有意義。
直到現(xiàn)在,為了在Windows下滿足上述約束,開(kāi)發(fā)者最常用的方法仍然是使用有名互斥體(named mutex)技術(shù)。然而采用這種技術(shù)來(lái)滿足上述約束存在以下缺點(diǎn):
該技術(shù)具有使互斥體的名字被其他應(yīng)用程序所使用的較小的、潛在的風(fēng)險(xiǎn)。在這種情況下該技術(shù)將不再有效并且會(huì)造成很難檢測(cè)到的bug。
該技術(shù)不能解決我們僅允許一個(gè)應(yīng)用程序產(chǎn)生N個(gè)實(shí)例這種一般的問(wèn)題。
幸而在System.Diagnostics.Process類中擁有GetCurrentProcess()(返回當(dāng)前進(jìn)程)和GetPro- cesses()(返回機(jī)器上所有的進(jìn)程)這樣的靜態(tài)方法。在下面的程序中我們?yōu)樯鲜鰡?wèn)題找到了一個(gè)優(yōu)雅且簡(jiǎn)單的解決方案。

通過(guò)方法參數(shù)指定了遠(yuǎn)程機(jī)器的名字后,GetProcesses()方法也可以返回遠(yuǎn)程機(jī)器上所有的進(jìn)程。
終止當(dāng)前進(jìn)程
可以調(diào)用System.Environment類中的靜態(tài)方法Exit(int exitCode)或FailFast(stringmessage)終止當(dāng)前進(jìn)程。Exit()方法是最好的選擇,它將徹底終止進(jìn)程并向操作系統(tǒng)返回指定的退出代碼值。之所以稱為徹底終止是因?yàn)楫?dāng)前對(duì)象的所有清理工作以及finally塊的執(zhí)行都將由不同的線程完成。當(dāng)然,終止進(jìn)程將花費(fèi)一定的時(shí)間。
顧名思義,F(xiàn)ailFast()方法可以迅速終止進(jìn)程。Exit()方法所做的預(yù)防措施將被它忽略。只有一個(gè)包含了指定信息的嚴(yán)重錯(cuò)誤會(huì)被操作系統(tǒng)記錄到日志中。你可能想要在探查問(wèn)題的時(shí)候使用該方法,因?yàn)榭梢詫⒃摮绦虻膹氐捉K止視為數(shù)據(jù)惡化的起因。
線程
在早期的操作系統(tǒng)中并沒(méi)有線程的概念,進(jìn)程是擁有資源和獨(dú)立運(yùn)行的最小單位,也是程序執(zhí)行的最小單位。任務(wù)調(diào)度采用的是時(shí)間片
輪轉(zhuǎn)的搶占式調(diào)度方式,而進(jìn)程是任務(wù)調(diào)度的最小單位,每個(gè)進(jìn)程有各自獨(dú)立的一塊內(nèi)存,使得各個(gè)進(jìn)程之間內(nèi)存地址相互隔離。
后來(lái),隨著計(jì)算機(jī)的發(fā)展,對(duì)CPU的要求越來(lái)越高,進(jìn)程之間的切換開(kāi)銷較大,已經(jīng)無(wú)法滿足越來(lái)越復(fù)雜的程序的要求了。于是就發(fā)明
了線程,線程是程序執(zhí)行中一個(gè)單一的順序控制流程,是程序執(zhí)行流的最小單元,是處理器調(diào)度和分派的基本單位。一個(gè)進(jìn)程可以有一個(gè)或
多個(gè)線程,各個(gè)線程之間共享程序的內(nèi)存空間(也就是所在進(jìn)程的內(nèi)存空間)。一個(gè)標(biāo)準(zhǔn)的線程由線程ID,當(dāng)前指令指針PC,寄存器和堆棧組
成。而進(jìn)程由內(nèi)存空間(代碼,數(shù)據(jù),進(jìn)程空間,打開(kāi)的文件)和一個(gè)或多個(gè)線程組成。
受托管的線程與 Windows線程
必須要了解,執(zhí)行.NET應(yīng)用的線程實(shí)際上仍然是Windows線程。但是,當(dāng)某個(gè)線程被CLR所知時(shí),我們將它稱為受托管的線程。具體來(lái)說(shuō),由受托管的代碼創(chuàng)建出來(lái)的線程就是受托管的線程。如果一個(gè)線程由非托管的代碼所創(chuàng)建,那么它就是非托管的線程。不過(guò),一旦該線程執(zhí)行了受托管的代碼它就變成了受托管的線程。
一個(gè)受托管的線程和非托管的線程的區(qū)別在于,CLR將創(chuàng)建一個(gè)System.Threading.Thread類的實(shí)例來(lái)代表并操作前者。在內(nèi)部實(shí)現(xiàn)中,CLR將一個(gè)包含了所有受托管線程的列表保存在一個(gè)叫做ThreadStore地方。
CLR確保每一個(gè)受托管的線程在任意時(shí)刻都在一個(gè)AppDomain中執(zhí)行,但是這并不代表一個(gè)線程將永遠(yuǎn)處在一個(gè)AppDomain中,它可以隨著時(shí)間的推移轉(zhuǎn)到其他的AppDomain中。關(guān)于AppDomain的概念參見(jiàn)4.1。
從安全的角度來(lái)看,一個(gè)受托管的線程的主用戶與底層的非托管線程中的Windows主用戶是無(wú)關(guān)的。
搶占式多任務(wù)處理
我們可以問(wèn)自己下面這個(gè)問(wèn)題: 我的計(jì)算機(jī)只有一個(gè)處理器,然而在任務(wù)管理器中我們卻可以看到數(shù)以百計(jì)的線程正同時(shí)運(yùn)行在機(jī)器上!這怎么可能呢?
多虧了搶占式多任務(wù)處理,通過(guò)它對(duì)線程的調(diào)度,使得上述問(wèn)題成為可能。調(diào)度器作為Windows內(nèi)核的一部分,將時(shí)間切片,分成一段段的時(shí)間片。這些時(shí)間間隔以毫秒為精度且長(zhǎng)度并不固定。針對(duì)每個(gè)處理器,每個(gè)時(shí)間片僅服務(wù)于單獨(dú)一個(gè)線程。線程的迅速執(zhí)行給我們?cè)斐闪怂鼈冊(cè)谕瑫r(shí)運(yùn)行的假象。我們?cè)趦蓚€(gè)時(shí)間片的間隔中進(jìn)行上下文切換。該方法的優(yōu)點(diǎn)在于,那些正在等待某些Windows資源的線程將不會(huì)浪費(fèi)時(shí)間片,直到資源有效為止。
之所以用搶占式這個(gè)形容詞來(lái)修飾這種多任務(wù)管理方式,是因?yàn)樵诖朔N方式下線程將被系統(tǒng)強(qiáng)制性中斷。那些對(duì)此比較好奇的人應(yīng)該了解到,在上下文切換的過(guò)程中,操作系統(tǒng)會(huì)在下一個(gè)線程將要執(zhí)行的代碼中插入一條跳轉(zhuǎn)到下一個(gè)上下文切換的指令。該指令是一個(gè)軟中斷,如果線程在遇到這條指令前就終止了(例如,它正在等待某個(gè)資源),那么該指定將被刪除而上下文切換也將提前發(fā)生。
搶占式多任務(wù)處理的主要缺點(diǎn)在于,必須使用一種同步機(jī)制來(lái)保護(hù)資源以避免它們被無(wú)序訪問(wèn)。除此之外,還有另一種多任務(wù)管理模型,被稱為協(xié)調(diào)式多任務(wù)管理,其中線程間的切換將由線程自己負(fù)責(zé)完成。該模型普遍認(rèn)為太過(guò)危險(xiǎn),原因在于線程間的切換不發(fā)生的風(fēng)險(xiǎn)太大。如我們?cè)?.2.8節(jié)中所解釋的那樣,該機(jī)制會(huì)在內(nèi)部使用以提升某些服務(wù)器的性能,例如SQL Server2005。但Windows操作系統(tǒng)僅僅實(shí)現(xiàn)了搶占式多任務(wù)處理。
進(jìn)程與線程的優(yōu)先級(jí)
某些任務(wù)擁有比其他任務(wù)更高的優(yōu)先級(jí),它們需要操作系統(tǒng)為它們申請(qǐng)更多的處理時(shí)間。例如,某些由主處理器負(fù)責(zé)的外圍驅(qū)動(dòng)器必須不能被中斷。另一類高優(yōu)先級(jí)的任務(wù)就是圖形用戶界面。事實(shí)上,用戶不喜歡等待用戶界面被重繪。
那些從Win32世界來(lái)的用戶知道在CLR的底層,也就是Windows操作系統(tǒng)中,可以為每個(gè)線程賦予一個(gè)0~31的優(yōu)先級(jí)。但你無(wú)法在.NET的世界中也使用這些數(shù)值,因?yàn)椋?/p>
它們無(wú)法描述自身的含義。
隨著時(shí)間的流逝這些值是非常容易變化的。
1. 進(jìn)程的優(yōu)先級(jí)
可以使用Process類中的類型為ProcessPriorityClass的PriorityClass{get;set;}屬性為進(jìn)程賦予一個(gè)優(yōu)先級(jí)。System.Diagnostics.ProcessPriorityClass枚舉包含以下值:

如果某個(gè)進(jìn)程中屬于Process類的PriorityBoostEnabled屬性的值為true(默認(rèn)值為true),那么當(dāng)該進(jìn)程占據(jù)前臺(tái)窗口的時(shí)候,它的優(yōu)先級(jí)將增加一個(gè)單位。只有當(dāng)Process類的實(shí)例引用的是本機(jī)進(jìn)程時(shí),才能夠訪問(wèn)該屬性。
可以通過(guò)以下操作利用任務(wù)管理器來(lái)改變一個(gè)進(jìn)程的優(yōu)先級(jí):在所選的進(jìn)程上點(diǎn)擊右鍵>設(shè)置優(yōu)先級(jí)>從提供的6個(gè)值(和上圖所述一致)中做出選擇。
Windows操作系統(tǒng)有一個(gè)優(yōu)先級(jí)為0的空閑進(jìn)程。該進(jìn)程不能被其他任何進(jìn)程使用。根據(jù)定義,進(jìn)程的活躍度用時(shí)間的百分比表示為:100%減去在空閑進(jìn)程中所耗費(fèi)時(shí)間的比率。
2. 線程的優(yōu)先級(jí)
每個(gè)線程可以結(jié)合它所屬進(jìn)程的優(yōu)先級(jí),并使用System.Threading.Thread類中類型為T(mén)hreadPriority的Priority{get;set;}屬性定義各自的優(yōu)先級(jí)。System.Threading.Thread- Priority包含以下枚舉值:

在大多數(shù)應(yīng)用程序中,不需要修改進(jìn)程和線程的優(yōu)先級(jí),它們的默認(rèn)值為Normal。
進(jìn)程與線程的區(qū)別
1. 線程是程序執(zhí)行的最小單位,而進(jìn)程是操作系統(tǒng)分配資源的最小單位;
2. 一個(gè)進(jìn)程由一個(gè)或多個(gè)線程組成,線程是一個(gè)進(jìn)程中代碼的不同執(zhí)行路線
3. 進(jìn)程之間相互獨(dú)立,但同一進(jìn)程下的各個(gè)線程之間共享程序的內(nèi)存空間(包括代碼段,數(shù)據(jù)集,堆等)及一些進(jìn)程級(jí)的資源(如打開(kāi)文件和信
號(hào)等),某進(jìn)程內(nèi)的線程在其他進(jìn)程不可見(jiàn);
4. 調(diào)度和切換:線程上下文切換比進(jìn)程上下文切換要快得多
一個(gè)程序至少一個(gè)進(jìn)程, 一個(gè)進(jìn)程至少一個(gè)線程
進(jìn)程有自己的獨(dú)立地址空間, 每啟動(dòng)一個(gè)進(jìn)程, 系統(tǒng)就會(huì)為它分配地址空間, 建立數(shù)據(jù)表來(lái)維護(hù)代碼段、堆棧段和數(shù)據(jù)段, 這種操作非常昂貴。
而線程是共享進(jìn)程中的數(shù)據(jù)的, 使用相同的地址空間, 因此CPU切換一個(gè)線程的花費(fèi)遠(yuǎn)比進(jìn)程要小很多, 同時(shí)創(chuàng)建一個(gè)線程的開(kāi)銷啊也比進(jìn)程要小很多。
線程之間的通信更方便, 同一進(jìn)程下的線程共享全局變量、靜態(tài)變量等數(shù)據(jù), 而進(jìn)程之間的通信需要以通信的方式進(jìn)行。
如何處理好同步與互斥是編寫(xiě)多線程程序的難點(diǎn)。
多進(jìn)程程序更健壯, 進(jìn)程有獨(dú)立的地址空間, 一個(gè)進(jìn)程崩潰后, 在保護(hù)模式下不會(huì)對(duì)其它進(jìn)程產(chǎn)生影響,
而線程只是一個(gè)進(jìn)程中的不同執(zhí)行路徑。線程有自己的堆棧和局部變量, 但線程之間沒(méi)有獨(dú)立的空間, 所以可能一個(gè)線程出現(xiàn)問(wèn)題, 進(jìn)而導(dǎo)致整個(gè)程序出現(xiàn)問(wèn)題
線程和進(jìn)程關(guān)系示意圖


總之,線程和進(jìn)程都是一種抽象的概念,線程是一種比進(jìn)程還小的抽象,線程和進(jìn)程都可用于實(shí)現(xiàn)并發(fā)。
在早期的操作系統(tǒng)中并沒(méi)有線程的概念,進(jìn)程是能擁有資源和獨(dú)立運(yùn)行的最小單位,也是程序執(zhí)行的最小單位,它相當(dāng)于
一個(gè)進(jìn)程里只有一個(gè)線程,進(jìn)程本身就是線程。所以線程有時(shí)被稱為輕量級(jí)進(jìn)程
后來(lái),隨著計(jì)算機(jī)的發(fā)展,對(duì)多個(gè)任務(wù)之間上下文切換的效率要求越來(lái)越高,就抽象出一個(gè)更小的概念-線程,一般一個(gè)進(jìn)程會(huì)有多個(gè)
(也可以是一個(gè))線程。

任務(wù)調(diào)度
大部分操作系統(tǒng)的任務(wù)調(diào)度是采用時(shí)間片輪轉(zhuǎn)的搶占式調(diào)度方式,也就是說(shuō)一個(gè)任務(wù)執(zhí)行一小段時(shí)間后強(qiáng)制暫停去執(zhí)行下一個(gè)任務(wù),每個(gè)
任務(wù)輪流執(zhí)行。任務(wù)執(zhí)行的一小段時(shí)間叫做時(shí)間片,任務(wù)正在執(zhí)行時(shí)的狀態(tài)叫運(yùn)行狀態(tài),任務(wù)執(zhí)行一段時(shí)間后強(qiáng)制暫停去執(zhí)行下一個(gè)任務(wù),被
暫停的任務(wù)就處于就緒狀態(tài),等待下一個(gè)屬于它的時(shí)間片的到來(lái)。這樣每個(gè)任務(wù)都能得到執(zhí)行,由于CPU的執(zhí)行效率非常高,時(shí)間片
非常短,在各個(gè)任務(wù)之間快速地切換,給人的感覺(jué)就是多個(gè)任務(wù)在“同時(shí)進(jìn)行”,這也就是我們所說(shuō)的并發(fā)

為何不使用多進(jìn)程而是使用多線程?
線程廉價(jià),線程啟動(dòng)比較快,退出比較快,對(duì)系統(tǒng)資源的沖擊也比較小。而且線程彼此分享了大部分核心對(duì)象(File Handle)的擁有權(quán)
如果使用多重進(jìn)程,但是不可預(yù)期,且測(cè)試?yán)щy
