激情欧美日韩一区二区,浪货撅高贱屁股求主人调教视频,精品无码成人片一区二区98,国产高清av在线播放,色翁荡息又大又硬又粗视频

進(jìn)程和線(xiàn)程的區別

時(shí)間:2020-12-12 09:29:37 常識 我要投稿

進(jìn)程和線(xiàn)程的區別

  在學(xué)習編程時(shí),通常初學(xué)者會(huì )分不清楚進(jìn)程和線(xiàn)程,其實(shí)弄清楚者兩個(gè)概念并不是很難,下面就跟YJBYS小編一起來(lái)看看吧。

  簡(jiǎn)而言之,一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線(xiàn)程.

  線(xiàn)程的劃分尺度小于進(jìn)程,使得多線(xiàn)程程序的并發(fā)性高。

  另外,進(jìn)程在執行過(guò)程中擁有獨立的內存單元,而多個(gè)線(xiàn)程共享內存,從而極大地提高了程序的運行效率。

  線(xiàn)程在執行過(guò)程中與進(jìn)程還是有區別的。每個(gè)獨立的線(xiàn)程有一個(gè)程序運行的入口、順序執行序列和程序的出口。但是線(xiàn)程不能夠獨立執行,必須依存在應用程序中,由應用程序提供多個(gè)線(xiàn)程執行控制。

  從邏輯角度來(lái)看,多線(xiàn)程的意義在于一個(gè)應用程序中,有多個(gè)執行部分可以同時(shí)執行。但操作系統并沒(méi)有將多個(gè)線(xiàn)程看做多個(gè)獨立的應用,來(lái)實(shí)現進(jìn)程的調度和管理以及資源分配。這就是進(jìn)程和線(xiàn)程的重要區別。

  進(jìn)程是具有一定獨立功能的程序關(guān)于某個(gè)數據集合上的一次運行活動(dòng),進(jìn)程是系統進(jìn)行資源分配和調度的一個(gè)獨立單位.

  線(xiàn)程是進(jìn)程的一個(gè)實(shí)體,是CPU調度和分派的基本單位,它是比進(jìn)程更小的能獨立運行的基本單位.線(xiàn)程自己基本上不擁有系統資源,只擁有一點(diǎn)在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個(gè)進(jìn)程的其他的線(xiàn)程共享進(jìn)程所擁有的全部資源.

  一個(gè)線(xiàn)程可以創(chuàng )建和撤銷(xiāo)另一個(gè)線(xiàn)程;同一個(gè)進(jìn)程中的多個(gè)線(xiàn)程之間可以并發(fā)執行.

  進(jìn)程和線(xiàn)程的主要差別在于它們是不同的操作系統資源管理方式。進(jìn)程有獨立的地址空間,一個(gè)進(jìn)程崩潰后,在保護模式下不會(huì )對其它進(jìn)程產(chǎn)生影響,而線(xiàn)程只是一個(gè)進(jìn)程中的不同執行路徑。線(xiàn)程有自己的堆棧和局部變量,但線(xiàn)程之間沒(méi)有單獨的地址空間,一個(gè)線(xiàn)程死掉就等于整個(gè)進(jìn)程死掉,所以多進(jìn)程的程序要比多線(xiàn)程的程序健壯,但在進(jìn)程切換時(shí),耗費資源較大,效率要差一些。但對于一些要求同時(shí)進(jìn)行并且又要共享某些變量的并發(fā)操作,只能用線(xiàn)程,不能用進(jìn)程。如果有興趣深入的話(huà),我建議你們看看《現代操作系統》或者《操作系統的設計與實(shí)現》。對就個(gè)問(wèn)題說(shuō)得比較清楚。

  5.1 簡(jiǎn)介

  進(jìn)程(process)是一塊包含了某些資源的內存區域。操作系統利用進(jìn)程把它的工作劃分為一些功能單元。

  進(jìn)程中所包含的一個(gè)或多個(gè)執行單元稱(chēng)為線(xiàn)程(thread)。進(jìn)程還擁有一個(gè)私有的虛擬地址空間,該空間僅能被它所包含的線(xiàn)程訪(fǎng)問(wèn)。

  當運行.NET程序時(shí),進(jìn)程還會(huì )把被稱(chēng)為CLR的軟件層包含到它的內存空間中。上一章曾經(jīng)對CLR做了詳細描述。該軟件層是在進(jìn)程創(chuàng )建期間由運行時(shí)宿主載入的(參見(jiàn)4.2.3節)。

  線(xiàn)程只能歸屬于一個(gè)進(jìn)程并且它只能訪(fǎng)問(wèn)該進(jìn)程所擁有的資源。當操作系統創(chuàng )建一個(gè)進(jìn)程后,該進(jìn)程會(huì )自動(dòng)申請一個(gè)名為主線(xiàn)程或首要線(xiàn)程的線(xiàn)程。主線(xiàn)程將執行運行時(shí)宿主, 而運行時(shí)宿主會(huì )負責載入CLR。

  應用程序(application)是由一個(gè)或多個(gè)相互協(xié)作的進(jìn)程組成的。例如,Visual Studio開(kāi)發(fā)環(huán)境就是利用一個(gè)進(jìn)程編輯源文件,并利用另一個(gè)進(jìn)程完成編譯工作的應用程序。

  在Windows NT/2000/XP操作系統下,我們可以通過(guò)任務(wù)管理器在任意時(shí)間查看所有的應用程序和進(jìn)程。盡管只打開(kāi)了幾個(gè)應用程序,但是通常情況下將有大約30個(gè)進(jìn)程同時(shí)運行。 事實(shí)上,為了管理當前的會(huì )話(huà)和任務(wù)欄以及其他一些任務(wù),系統執行了大量的進(jìn)程。

  5.2 進(jìn)程

  5.2.1 簡(jiǎn)介

  在運行于32位處理器上的32位Windows操作系統中,可將一個(gè)進(jìn)程視為一段大小為4GB(232字節)的線(xiàn)性?xún)却婵臻g,它起始于0x00000000結束于0xFFFFFFFF。這段內存空間不能被其他進(jìn)程所訪(fǎng)問(wèn),所以稱(chēng)為該進(jìn)程的私有空間。這段空間被平分為兩塊,2GB被系統所有,剩下2GB被用戶(hù)所有。

  如果有N個(gè)進(jìn)程運行在同一臺機器上,那么將需要N×4GB的海量RAM,還好事實(shí)并非如此。

  Windows是按需為每個(gè)進(jìn)程分配內存的,4GB是32位系統中一個(gè)進(jìn)程所占空間的上限。

  將進(jìn)程所需的內存劃分為4KB大小的內存頁(yè),并根據使用情況將這些內存頁(yè)存儲在硬盤(pán)上或加載到RAM中,通過(guò)系統的這種虛擬內存機制,我們可以有效地減少對實(shí)際內存的需求量。當然這些對用戶(hù)和開(kāi)發(fā)者來(lái)說(shuō)都是透明的。

  5.2.2 System.Diagnostics.Process類(lèi)

  System.Diagnostics.Process類(lèi)的實(shí)例可以引用一個(gè)進(jìn)程,被引用的進(jìn)程包含以下幾種。

  該實(shí)例的當前進(jìn)程。

  本機上除了當前進(jìn)程的其他進(jìn)程。

  遠程機器上的某個(gè)進(jìn)程。

  通過(guò)該類(lèi)所包含的方法和字段,可以創(chuàng )建或銷(xiāo)毀一個(gè)進(jìn)程,并且可以獲得一個(gè)進(jìn)程的相關(guān)信息。下面將討論一些使用該類(lèi)實(shí)現的常見(jiàn)任務(wù)。

  5.2.3 創(chuàng )建和銷(xiāo)毀子進(jìn)程

  下面的程序創(chuàng )建了一個(gè)稱(chēng)為子進(jìn)程的新進(jìn)程。在這種情況下,初始的進(jìn)程稱(chēng)為父進(jìn)程。子進(jìn)程啟動(dòng)了一個(gè)記事本應用程序。父進(jìn)程的線(xiàn)程在等待1秒后銷(xiāo)毀該子進(jìn)程。該程序的執行效果就是打開(kāi)并關(guān)閉記事本。

  例5-1

  靜態(tài)方法Start()可以使用已存在的Windows文件擴展名關(guān)聯(lián)機制。例如,我們可以利用下面的代碼執行同樣的操作。

  默認情況下,子進(jìn)程將繼承其父進(jìn)程的安全上下文。但還可以使用Process.Start()方法的一個(gè)重載版本在任意用戶(hù)的安全上下文中啟動(dòng)該子進(jìn)程,當然需要通過(guò)一個(gè)System.Diagnostics. ProcessStartInfo類(lèi)的實(shí)例來(lái)提供該用戶(hù)的用戶(hù)名和密碼。

  5.2.4 避免在一臺機器上同時(shí)運行同一應用程序的多個(gè)實(shí)例

  有些應用程序需要這種功能。實(shí)際上,通常來(lái)說(shuō)在同一臺機器上同時(shí)運行一個(gè)應用程序的多個(gè)實(shí)例并沒(méi)有意義。

  直到現在,為了在Windows下滿(mǎn)足上述約束,開(kāi)發(fā)者最常用的方法仍然是使用有名互斥體(named mutex)技術(shù)(參見(jiàn)5.7.2節)。然而采用這種技術(shù)來(lái)滿(mǎn)足上述約束存在以下缺點(diǎn):

  該技術(shù)具有使互斥體的名字被其他應用程序所使用的較小的、潛在的風(fēng)險。在這種情況下該技術(shù)將不再有效并且會(huì )造成很難檢測到的bug。

  該技術(shù)不能解決我們僅允許一個(gè)應用程序產(chǎn)生N個(gè)實(shí)例這種一般的問(wèn)題。

  幸而在System.Diagnostics.Process類(lèi)中擁有GetCurrentProcess()(返回當前進(jìn)程)和GetPro- cesses()(返回機器上所有的進(jìn)程)這樣的靜態(tài)方法。在下面的程序中我們?yōu)樯鲜鰡?wèn)題找到了一個(gè)優(yōu)雅且簡(jiǎn)單的解決方案。

  例5-2

  通過(guò)方法參數指定了遠程機器的名字后,GetProcesses()方法也可以返回遠程機器上所有的進(jìn)程。

  5.2.5 終止當前進(jìn)程

  可以調用System.Environment類(lèi)中的靜態(tài)方法Exit(int exitCode)或FailFast(stringmessage)終止當前進(jìn)程。Exit()方法是最好的選擇,它將徹底終止進(jìn)程并向操作系統返回指定的退出代碼值。之所以稱(chēng)為徹底終止是因為當前對象的所有清理工作以及finally塊的執行都將由不同的線(xiàn)程完成。當然,終止進(jìn)程將花費一定的時(shí)間。

  顧名思義,FailFast()方法可以迅速終止進(jìn)程。Exit()方法所做的預防措施將被它忽略。只有一個(gè)包含了指定信息的嚴重錯誤會(huì )操作系統記錄到日志中。你可能想要在探查問(wèn)題的時(shí)候使用該方法,因為可以將該程序的徹底終止視為數據惡化的起因。

  5.3 線(xiàn)程

  5.3.1 簡(jiǎn)介

  一個(gè)線(xiàn)程包含以下內容。

  一個(gè)指向當前被執行指令的指令指針;

  一個(gè)棧;

  一個(gè)寄存器值的集合,定義了一部分描述正在執行線(xiàn)程的處理器狀態(tài)的.值;

  一個(gè)私有的數據區。

  所有這些元素都歸于線(xiàn)程執行上下文的名下。處在同一個(gè)進(jìn)程中的所有線(xiàn)程都可以訪(fǎng)問(wèn)該進(jìn)程所包含的地址空間,當然也包含存儲在該空間中的所有資源。

  我們不準備討論線(xiàn)程在內核模式或者用戶(hù)模式執行的問(wèn)題。盡管.NET以前的Windows一直使用這兩種模式,并且依然存在,但是對.NET Framework來(lái)說(shuō)它們是不可見(jiàn)的。

  并行使用一些線(xiàn)程通常是我們在實(shí)現算法時(shí)的自然反應。實(shí)際上,一個(gè)算法往往由一系列可以并發(fā)執行的任務(wù)組成。但是需要引起注意的是,使用大量的線(xiàn)程將引起過(guò)多的上下文切換,最終反而影響了性能。

  同樣,幾年前我們就注意到,預測每18個(gè)月處理器運算速度增加一倍的摩爾定律已不再成立。處理器的頻率停滯在3GHz~4GHz上下。這是由于物理上的限制,需要一段時(shí)間才能取得突破。同時(shí),為了在性能競爭中不會(huì )落敗,較大的處理器制造商如AMD和Intel目前都將目標轉向多核芯片。因此我們可以預計在接下去的幾年中這種類(lèi)型的架構將廣泛被采用。在這種情況下,改進(jìn)應用性能的唯一方案就是合理地利用多線(xiàn)程技術(shù)。

  5.3.2 受托管的線(xiàn)程與 Windows線(xiàn)程

  必須要了解,執行.NET應用的線(xiàn)程實(shí)際上仍然是Windows線(xiàn)程。但是,當某個(gè)線(xiàn)程被CLR所知時(shí),我們將它稱(chēng)為受托管的線(xiàn)程。具體來(lái)說(shuō),由受托管的代碼創(chuàng )建出來(lái)的線(xiàn)程就是受托管的線(xiàn)程。如果一個(gè)線(xiàn)程由非托管的代碼所創(chuàng )建,那么它就是非托管的線(xiàn)程。不過(guò),一旦該線(xiàn)程執行了受托管的代碼它就變成了受托管的線(xiàn)程。

  一個(gè)受托管的線(xiàn)程和非托管的線(xiàn)程的區別在于,CLR將創(chuàng )建一個(gè)System.Threading.Thread類(lèi)的實(shí)例來(lái)代表并操作前者。在內部實(shí)現中,CLR將一個(gè)包含了所有受托管線(xiàn)程的列表保存在一個(gè)叫做ThreadStore地方。

  CLR確保每一個(gè)受托管的線(xiàn)程在任意時(shí)刻都在一個(gè)AppDomain中執行,但是這并不代表一個(gè)線(xiàn)程將永遠處在一個(gè)AppDomain中,它可以隨著(zhù)時(shí)間的推移轉到其他的AppDomain中。關(guān)于A(yíng)ppDomain的概念參見(jiàn)4.1。

  從安全的角度來(lái)看,一個(gè)受托管的線(xiàn)程的主用戶(hù)與底層的非托管線(xiàn)程中的Windows主用戶(hù)是無(wú)關(guān)的。

  5.3.3 搶占式多任務(wù)處理

  我們可以問(wèn)自己下面這個(gè)問(wèn)題: 我的計算機只有一個(gè)處理器,然而在任務(wù)管理器中我們卻可以看到數以百計的線(xiàn)程正同時(shí)運行在機器上!這怎么可能呢?

  多虧了搶占式多任務(wù)處理,通過(guò)它對線(xiàn)程的調度,使得上述問(wèn)題成為可能。調度器作為Windows內核的一部分,將時(shí)間切片,分成一段段的時(shí)間片。這些時(shí)間間隔以毫秒為精度且長(cháng)度并不固定。針對每個(gè)處理器,每個(gè)時(shí)間片僅服務(wù)于單獨一個(gè)線(xiàn)程。線(xiàn)程的迅速執行給我們造成了它們在同時(shí)運行的假象。我們在兩個(gè)時(shí)間片的間隔中進(jìn)行上下文切換。該方法的優(yōu)點(diǎn)在于,那些正在等待某些Windows資源的線(xiàn)程將不會(huì )浪費時(shí)間片,直到資源有效為止。

  之所以用搶占式這個(gè)形容詞來(lái)修飾這種多任務(wù)管理方式,是因為在此種方式下線(xiàn)程將被系統強制性中斷。那些對此比較好奇的人應該了解到,在上下文切換的過(guò)程中,操作系統會(huì )在下一個(gè)線(xiàn)程將要執行的代碼中插入一條跳轉到下一個(gè)上下文切換的指令。該指令是一個(gè)軟中斷,如果線(xiàn)程在遇到這條指令前就終止了(例如,它正在等待某個(gè)資源),那么該指定將被刪除而上下文切換也將提前發(fā)生。

  搶占式多任務(wù)處理的主要缺點(diǎn)在于,必須使用一種同步機制來(lái)保護資源以避免它們被無(wú)序訪(fǎng)問(wèn)。除此之外,還有另一種多任務(wù)管理模型,被稱(chēng)為協(xié)調式多任務(wù)管理,其中線(xiàn)程間的切換將由線(xiàn)程自己負責完成。該模型普遍認為太過(guò)危險,原因在于線(xiàn)程間的切換不發(fā)生的風(fēng)險太大。如我們在4.2.8節中所解釋的那樣,該機制會(huì )在內部使用以提升某些服務(wù)器的性能,例如SQL Server2005。但Windows操作系統僅僅實(shí)現了搶占式多任務(wù)處理。

  5.3.4 進(jìn)程與線(xiàn)程的優(yōu)先級

  某些任務(wù)擁有比其他任務(wù)更高的優(yōu)先級,它們需要操作系統為它們申請更多的處理時(shí)間。例如,某些由主處理器負責的外圍驅動(dòng)器必須不能被中斷。另一類(lèi)高優(yōu)先級的任務(wù)就是圖形用戶(hù)界面。事實(shí)上,用戶(hù)不喜歡等待用戶(hù)界面被重繪。

  那些從Win32世界來(lái)的用戶(hù)知道在CLR的底層,也就是Windows操作系統中,可以為每個(gè)線(xiàn)程賦予一個(gè)0~31的優(yōu)先級。但你無(wú)法在.NET的世界中也使用這些數值,因為:

  它們無(wú)法描述自身的含義。

  隨著(zhù)時(shí)間的流逝這些值是非常容易變化的。

  1. 進(jìn)程的優(yōu)先級

  可以使用Process類(lèi)中的類(lèi)型為ProcessPriorityClass的PriorityClass{get;set;}屬性為進(jìn)程賦予一個(gè)優(yōu)先級。System.Diagnostics.ProcessPriorityClass枚舉包含以下值:

  如果某個(gè)進(jìn)程中屬于Process類(lèi)的PriorityBoostEnabled屬性的值為true(默認值為true),那么當該進(jìn)程占據前臺窗口的時(shí)候,它的優(yōu)先級將增加一個(gè)單位。只有當Process類(lèi)的實(shí)例引用的是本機進(jìn)程時(shí),才能夠訪(fǎng)問(wèn)該屬性。

  可以通過(guò)以下操作利用任務(wù)管理器來(lái)改變一個(gè)進(jìn)程的優(yōu)先級:在所選的進(jìn)程上點(diǎn)擊右鍵>設置優(yōu)先級>從提供的6個(gè)值(和上圖所述一致)中做出選擇。

  Windows操作系統有一個(gè)優(yōu)先級為0的空閑進(jìn)程。該進(jìn)程不能被其他任何進(jìn)程使用。根據定義,進(jìn)程的活躍度用時(shí)間的百分比表示為:100%減去在空閑進(jìn)程中所耗費時(shí)間的比率。

  2. 線(xiàn)程的優(yōu)先級

  每個(gè)線(xiàn)程可以結合它所屬進(jìn)程的優(yōu)先級,并使用System.Threading.Thread類(lèi)中類(lèi)型為T(mén)hreadPriority的Priority{get;set;}屬性定義各自的優(yōu)先級。System.Threading.Thread- Priority包含以下枚舉值:

  在大多數應用程序中,不需要修改進(jìn)程和線(xiàn)程的優(yōu)先級,它們的默認值為Normal。

  5.3.5 System.Threading.Thread類(lèi)

  CLR會(huì )自動(dòng)將一個(gè)System.Threading.Thread類(lèi)的實(shí)例與各個(gè)受托管的線(xiàn)程關(guān)聯(lián)起來(lái)?梢允褂迷搶ο髲木(xiàn)程自身或從其他線(xiàn)程來(lái)操縱線(xiàn)程。還可以通過(guò)System.Threading.Thread類(lèi)的靜態(tài)屬性CurrentThread來(lái)獲得當前線(xiàn)程的對象。

  Thread類(lèi)有一個(gè)功能使我們能夠很方便的調試多線(xiàn)程應用程序,該功能允許我們使用一個(gè)字符串為線(xiàn)程命名:

  5.3.6 創(chuàng )建與Join一個(gè)線(xiàn)程

  只需通過(guò)創(chuàng )建一個(gè)Thread類(lèi)的實(shí)例,就可以在當前的進(jìn)程中創(chuàng )建一個(gè)新的線(xiàn)程。該類(lèi)擁有多個(gè)構造函數,它們將接受一個(gè)類(lèi)型為System.Threading.ThreadStart或System.Threading.Parame-trizedThreadStart的委托對象作為參數,線(xiàn)程被創(chuàng )建出來(lái)后首先執行該委托對象所引用的方法。使用ParametrizedThreadStart類(lèi)型的委托對象允許用戶(hù)為新線(xiàn)程將要執行的方法傳入一個(gè)對象作為參數。Thread類(lèi)的一些構造函數還接受一個(gè)整型參數用于設置線(xiàn)程要使用的最大棧的大小,該值至少為128KB(即131072字節)。創(chuàng )建了Thread類(lèi)型的實(shí)例后,必須調用Thread.Start()方法以真正啟動(dòng)這個(gè)線(xiàn)程。

  例5-3

  該程序輸出:

  在這個(gè)例子中,我們使用Join()方法掛起當前線(xiàn)程,直到調用Join()方法的線(xiàn)程執行完畢。該方法還存在包含參數的重載版本,其中的參數用于指定等待線(xiàn)程結束的最長(cháng)時(shí)間(即超時(shí))所花費的毫秒數。如果線(xiàn)程中的工作在規定的超時(shí)時(shí)段內結束,該版本的Join()方法將返回一個(gè)布爾量True。

  5.3.7 掛起一個(gè)線(xiàn)程

  可以使用Thread類(lèi)的Sleep()方法將一個(gè)正在執行的線(xiàn)程掛起一段特定的時(shí)間,還可以通過(guò)一個(gè)以毫秒為單位的整型值或者一個(gè)System.TimeSpan結構的實(shí)例設定這段掛起的時(shí)間。該結構的一個(gè)實(shí)例可以設定一個(gè)精度為1/10 ms(100ns)的時(shí)間段,但是Sleep()方法的最高精度只有1ms。

  我們也可以從將要掛起的線(xiàn)程自身或者另一個(gè)線(xiàn)程中使用Thread類(lèi)的Suspend()方法將一個(gè)線(xiàn)程的活動(dòng)掛起。在這兩種情況中,線(xiàn)程都將被阻塞直到另一個(gè)線(xiàn)程調用了Resume()方法。相對于Sleep()方法,Suspend()方法不會(huì )立即將線(xiàn)程掛起,而是在線(xiàn)程到達下一個(gè)安全點(diǎn)之后,CLR才會(huì )將該線(xiàn)程掛起。安全點(diǎn)的概念參見(jiàn)4.7.11節。

  5.3.8 終止一個(gè)線(xiàn)程

  一個(gè)線(xiàn)程可以在以下場(chǎng)景中將自己終止。

  從自己開(kāi)始執行的方法(主線(xiàn)程中的Main()方法,其他線(xiàn)程中ThreadStart委托對象所引用的方法)中退出。

  被自己終止。

  被另一個(gè)線(xiàn)程終止。

  第一種情況不太重要,我們將主要關(guān)注另兩種情況。在這兩種情況中,都可以使用Abort()方法(通過(guò)當前線(xiàn)程或從當前線(xiàn)程之外的一個(gè)線(xiàn)程)。使用該方法將在線(xiàn)程中引發(fā)一個(gè)類(lèi)型為T(mén)hreadAbortException的異常。由于線(xiàn)程正處于一種被稱(chēng)為AbortRequested的特殊狀態(tài),該異常具有一個(gè)特殊之處:當它被異常處理所捕獲后,將自動(dòng)被重新拋出。只有在異常處理中調用Thread.ResetAbort()這個(gè)靜態(tài)方法(如果我們有足夠的權限)才能阻止它的傳播。

  例5-4 主線(xiàn)程的自殺

  當線(xiàn)程A對線(xiàn)程B調用了Abort()方法,建議調用B的Join()方法,讓A一直等待直到B終止。Interrupt()方法也可以將一個(gè)處于阻塞狀態(tài)的線(xiàn)程(即由于調用了Wait()、Sleep()或者Join()其中一個(gè)方法而阻塞)終止。該方法會(huì )根據要被終止的線(xiàn)程是否處于阻塞狀態(tài)而表現出不同的行為。

  如果該方法被另一個(gè)線(xiàn)程調用時(shí),要被終止的線(xiàn)程處于阻塞狀態(tài),那么會(huì )產(chǎn)生ThreadInterruptedException異常。

  如果該方法被另一個(gè)線(xiàn)程調用時(shí),要被終止的線(xiàn)程不處于阻塞狀態(tài),那么一旦該線(xiàn)程進(jìn)入阻塞狀態(tài),就會(huì )引發(fā)異常。這種行為與線(xiàn)程對自己調用Interrupt()方法是一樣的。

  5.3.9 前臺線(xiàn)程與后臺線(xiàn)程

  Thread類(lèi)提供了IsBackground{get;set}的布爾屬性。當前臺線(xiàn)程還在運行時(shí),它會(huì )阻止進(jìn)程被終止。另一方面,一旦所指的進(jìn)程中不再有前臺線(xiàn)程,后臺線(xiàn)程就會(huì )被CLR自動(dòng)終止(調用Abort()方法)。IsBackground的默認值為false,這意味著(zhù)所有的線(xiàn)程默認情況處于前臺狀態(tài)。

  5.3.10 受托管線(xiàn)程

  Thread類(lèi)擁有一個(gè)System.Threading.ThreadState枚舉類(lèi)型的字段ThreadState,它包含以下枚舉值:

  有關(guān)每個(gè)狀態(tài)的具體描述可以在MSDN上一篇名為“ThreadStateEnumeration”的文章中找到。該枚舉類(lèi)型是一個(gè)二進(jìn)制位域,這表示一個(gè)該類(lèi)型的實(shí)例可以同時(shí)表示多個(gè)枚舉值。例如,一個(gè)線(xiàn)程可以同時(shí)處于Running、AbortRequested和Background這三種狀態(tài)。二進(jìn)制位域的概念參見(jiàn)10.11.3節。

  根據我們在前面的章節中所了解的知識,我們定義了如圖5-1所示的簡(jiǎn)化的狀態(tài)圖。

  圖5-1 簡(jiǎn)化的托管線(xiàn)程

  5.4 訪(fǎng)問(wèn)資源同步簡(jiǎn)介

  在多線(xiàn)程應用(一個(gè)或多個(gè)處理器)的計算中會(huì )使用到同步這個(gè)詞。實(shí)際上,這些應用程序的特點(diǎn)就是它們擁有多個(gè)執行單元,而這些單元在訪(fǎng)問(wèn)資源的時(shí)候可能會(huì )發(fā)生沖突。線(xiàn)程間會(huì )共享同步對象,而同步對象的目的在于能夠阻塞一個(gè)或多個(gè)線(xiàn)程,直到另一個(gè)線(xiàn)程使得某個(gè)特定條件得到滿(mǎn)足。

  我們將看到,存在多種同步類(lèi)與同步機制,每種制針對一個(gè)或一些特定的需求。如果要利用同步構建一個(gè)復雜的多線(xiàn)程應用程序,那么很有必要先掌握本章的內容。我們將在下面的內容中盡力區分他們,尤其要指出那些在各個(gè)機制間最微妙的區別。

  合理地同步一個(gè)程序是最精細的軟件開(kāi)發(fā)任務(wù)之一,單這一個(gè)主題就足以寫(xiě)幾本書(shū)。在深入到細節之前,應該首先確認使用同步是否不可避免。通常,使用一些簡(jiǎn)單的規則可以讓我們遠離同步問(wèn)題。在這些規則中有線(xiàn)程與資源的親緣性規則,我們將在稍后介紹。

  應該意識到,對程序中資源的訪(fǎng)問(wèn)進(jìn)行同步時(shí),其難點(diǎn)來(lái)自于是使用細粒度鎖還是粗粒度鎖這個(gè)兩難的選擇。如果在訪(fǎng)問(wèn)資源時(shí)采用粗粒度的同步方式,雖然可以簡(jiǎn)化代碼但是也會(huì )把自己暴露在爭用瓶頸的問(wèn)題上。如果粒度過(guò)細,代碼又會(huì )變的很復雜,以至于維護工作令人生厭。然后又會(huì )遇上死鎖和競態(tài)條件這些在下面章節將要介紹的問(wèn)題。

  因此在我們開(kāi)始談?wù)撚嘘P(guān)同步機制之前,有必要先了解一下有關(guān)競態(tài)條件和死鎖的概念。

  5.4.1 競態(tài)條件

  競態(tài)條件指的是一種特殊的情況,在這種情況下各個(gè)執行單元以一種沒(méi)有邏輯的順序執行動(dòng)作,從而導致意想不到的結果。

  舉一個(gè)例子,線(xiàn)程T修改資源R后,釋放了它對R的寫(xiě)訪(fǎng)問(wèn)權,之后又重新奪回R的讀訪(fǎng)問(wèn)權再使用它,并以為它的狀態(tài)仍然保持在它釋放它之后的狀態(tài)。但是在寫(xiě)訪(fǎng)問(wèn)權釋放后到重新奪回讀訪(fǎng)問(wèn)權的這段時(shí)間間隔中,可能另一個(gè)線(xiàn)程已經(jīng)修改了R的狀態(tài)。

  另一個(gè)經(jīng)典的競態(tài)條件的例子就是生產(chǎn)者/消費者模型。生產(chǎn)者通常使用同一個(gè)物理內存空間保存被生產(chǎn)的信息。一般說(shuō)來(lái),我們不會(huì )忘記在生產(chǎn)者與消費者的并發(fā)訪(fǎng)問(wèn)之間保護這個(gè)空間。容易被我們忘記的是生產(chǎn)者必須確保在生產(chǎn)新信息前,舊的信息已被消費者所讀取。如果我們沒(méi)有采取相應的預防措施,我們將面臨生產(chǎn)的信息從未被消費的危險。

  如果靜態(tài)條件沒(méi)有被妥善的管理,將導致安全系統的漏洞。同一個(gè)應用程序的另一個(gè)實(shí)例很可能會(huì )引發(fā)一系列開(kāi)發(fā)者所預計不到的事件。一般來(lái)說(shuō),必須對那種用于確認身份鑒別結果的布爾量的寫(xiě)訪(fǎng)問(wèn)做最完善的保護。如果沒(méi)有這么做,那么在它的狀態(tài)被身份鑒別機制設置后,到它被讀取以保護對資源的訪(fǎng)問(wèn)的這段時(shí)間內,很有可能已經(jīng)被修改了。已知的安全漏洞很多都歸咎于對靜態(tài)條件不恰當的管理。其中之一甚至影響了Unix操作系統的內核。

  5.4.2 死鎖

  死鎖指的是由于兩個(gè)或多個(gè)執行單元之間相互等待對方結束而引起阻塞的情況。例如:

  一個(gè)線(xiàn)程T1獲得了對資源R1的訪(fǎng)問(wèn)權。

  一個(gè)線(xiàn)程T2獲得了對資源R2的訪(fǎng)問(wèn)權。

  T1請求對R2的訪(fǎng)問(wèn)權但是由于此權力被T2所占而不得不等待。

  T2請求對R1的訪(fǎng)問(wèn)權但是由于此權力被T1所占而不得不等待。

  T1和T2將永遠維持等待狀態(tài),此時(shí)我們陷入了死鎖的處境!這種問(wèn)題比你所遇到的大多數的bug都要隱秘,針對此問(wèn)題主要有三種解決方案:

  在同一時(shí)刻不允許一個(gè)線(xiàn)程訪(fǎng)問(wèn)多個(gè)資源。

  為資源訪(fǎng)問(wèn)權的獲取定義一個(gè)關(guān)系順序。換句話(huà)說(shuō),當一個(gè)線(xiàn)程已經(jīng)獲得了R1的訪(fǎng)問(wèn)權后,將無(wú)法獲得R2的訪(fǎng)問(wèn)權。當然,訪(fǎng)問(wèn)權的釋放必須遵循相反的順序。

  為所有訪(fǎng)問(wèn)資源的請求系統地定義一個(gè)最大等待時(shí)間(超時(shí)時(shí)間),并妥善處理請求失敗的情況。幾乎所有的.NET的同步機制都提供了這個(gè)功能。

  前兩種技術(shù)效率更高但是也更加難于實(shí)現。事實(shí)上,它們都需要很強的約束,而這點(diǎn)隨著(zhù)應用程序的演變將越來(lái)越難以維護。盡管如此,使用這些技術(shù)不會(huì )存在失敗的情況。

  大的項目通常使用第三種方法。事實(shí)上,如果項目很大,一般來(lái)說(shuō)它會(huì )使用大量的資源。在這種情況下,資源之間發(fā)生沖突的概率很低,也就意味著(zhù)失敗的情況會(huì )比較罕見(jiàn)。我們認為這是一種樂(lè )觀(guān)的方法。秉著(zhù)同樣的精神,我們在19.5節描述了一種樂(lè )觀(guān)的數據庫訪(fǎng)問(wèn)模型。

  5.5 使用volatile字段與Interlocked類(lèi)實(shí)現同步

  5.5.1 volatile字段

  volatile字段可以被多個(gè)線(xiàn)程訪(fǎng)問(wèn)。我們假設這些訪(fǎng)問(wèn)沒(méi)有做任何同步。在這種情況下,CLR中一些用于管理代碼和內存的內部機制將負責同步工作,但是此時(shí)不能確保對該字段讀訪(fǎng)問(wèn)總能讀取到最新的值,而聲明為volatile的字段則能提供這樣的保證。在C#中,如果一個(gè)字段在它的聲明前使用了volatile關(guān)鍵字,則該字段被聲明為volatile。

  不是所有的字段都可以成為volatile,成為這種類(lèi)型的字段有一個(gè)條件。如果一個(gè)字段要成為volatile,它的類(lèi)型必須是以下所列的類(lèi)型中的一種:

  引用類(lèi)型(這里只有訪(fǎng)問(wèn)該類(lèi)型的引用是同步的,訪(fǎng)問(wèn)其成員并不同步)。

  一個(gè)指針(在不安全的代碼塊中)。

  sbyte、byte、short、ushort、int、uint、char、float、bool(工作在64位處理器上時(shí)為double、long與ulong)。

  一個(gè)使用以下底層類(lèi)型的枚舉類(lèi)型:byte、sbyte、short、ushort、int、uint(工作在64位的處理器上時(shí)為double、long與ulong)。

  你可能已經(jīng)注意到了,只有值或者引用的位數不超過(guò)本機整型值的位數(4或8由底層處理器決定)的類(lèi)型才能成為volatile。這意味著(zhù)對更大的值類(lèi)型進(jìn)行并發(fā)訪(fǎng)問(wèn)必須進(jìn)行同步,下面我們將會(huì )對此進(jìn)行討論。

  5.5.2 System.Threading.Interlocked類(lèi)

  經(jīng)驗顯示,那些需要在多線(xiàn)程情況下被保護的資源通常是整型值,而這些被共享的整型值最常見(jiàn)的操作就是增加/減少以及相加。.NETFramework利用System.Threading.Interlocked類(lèi)提供了一個(gè)專(zhuān)門(mén)的機制用于完成這些特定的操作。這個(gè)類(lèi)提供了Increment()、Decrement()與Add()三個(gè)靜態(tài)方法,分別用于對int或者long類(lèi)型變量的遞增、遞減與相加操作,這些變量以引用方式作為參數傳入。我們認為使用Interlocked類(lèi)讓這些操作具有了原子性。

  下面的程序顯示了兩個(gè)線(xiàn)程如何并發(fā)訪(fǎng)問(wèn)一個(gè)名為counter的整型變量。一個(gè)線(xiàn)程將其遞增5次,另一個(gè)將其遞減5次。

  例5-5

  該程序輸出(以非確定方式輸出,意味著(zhù)每執行一次顯示的結果都是不同的):

  如果我們不讓這些線(xiàn)程在每次修改變量后休眠10毫秒,那么它們將有足夠的時(shí)間在一個(gè)時(shí)間片中完成它們的任務(wù),那樣也就不會(huì )出現交叉操作,更不用說(shuō)并發(fā)訪(fǎng)問(wèn)了。

  5.5.3 Interlocked類(lèi)提供的其他功能

  Interlocked類(lèi)還允許使用Exchange()靜態(tài)方法,以原子操作的形式交換某些變量的狀態(tài)。還可以使用CompareExchange()靜態(tài)方法在滿(mǎn)足一個(gè)特定條件的基礎上以原子操作的形式交換兩個(gè)值。

  5.6 使用System.Threading.Monitor類(lèi)與C#的lock關(guān)鍵字實(shí)現同步

  以原子操作的方式完成簡(jiǎn)單的操作無(wú)疑是很重要的,但是這還遠不能涵蓋所有需要用到同步的事例。System.Threading.Monitor類(lèi)幾乎允許將任意一段代碼設置為在某個(gè)時(shí)間僅能被一個(gè)線(xiàn)程執行。我們將這段代碼稱(chēng)之為臨界區。

  5.6.1 Enter()方法和Exit()方法

  Monitor類(lèi)提供了Enter(object)與Exit(object)這兩個(gè)靜態(tài)方法。這兩個(gè)方法以一個(gè)對象作為參數,該對象提供了一個(gè)簡(jiǎn)單的方式用于唯一標識那個(gè)將以同步方式訪(fǎng)問(wèn)的資源。當一個(gè)線(xiàn)程調用了Enter()方法,它將等待以獲得訪(fǎng)問(wèn)該引用對象的獨占權(僅當另一個(gè)線(xiàn)程擁有該權力的時(shí)候它才會(huì )等待)。一旦該權力被獲得并使用,線(xiàn)程可以對同一個(gè)對象調用Exit()方法以釋放該權力。

  一個(gè)線(xiàn)程可以對同一個(gè)對象多次調用Enter(),只要對同一對象調用相同次數的Exit()來(lái)釋放獨占訪(fǎng)問(wèn)權。

  一個(gè)線(xiàn)程也可以在同一時(shí)間擁有多個(gè)對象的獨占權,但是這樣會(huì )產(chǎn)生死鎖的情況。

  絕不能對一個(gè)值類(lèi)型的實(shí)例調用Enter()與Exit()方法。

  不管發(fā)生了什么,必須在finally子句中調用Exit()以釋放所有的獨占訪(fǎng)問(wèn)權。

  如果在例5-5中,一個(gè)線(xiàn)程非要將counter做一次平方而另一個(gè)線(xiàn)程非要將counter乘2,我們就不得不用Monitor類(lèi)去替換對Interlocked類(lèi)的使用。f1()與f2()的代碼將變成下面這樣:

  例5-6[1]

  人們很容易想到用counter來(lái)代替typeof(Program),但是counter是一個(gè)值類(lèi)型的靜態(tài)成員。需要注意平方和倍增操作是不滿(mǎn)足交換律的,所以counter的最終結果是非確定性的。

  5.6.2 C#的lock關(guān)鍵字

  C#語(yǔ)言通過(guò)lock關(guān)鍵字提供了一種比使用Enter()和Exit()方法更加簡(jiǎn)潔的選擇。我們的程序可以改寫(xiě)為下面這個(gè)樣子:

  例5-7

  和for以及if關(guān)鍵字一樣,如果被lock關(guān)鍵字定義的塊僅包含一條指令,就不再需要花括號。我們可以再次改寫(xiě)為:

  使用lock關(guān)鍵字將引導C#編譯器創(chuàng )建出相應的try/finally塊,這樣仍舊可以預期到任何可能引發(fā)的異常?梢允褂肦eflector或者ildasm.exe工具驗證這一點(diǎn)。

  5.6.3 SyncRoot模式

  和前面的例子一樣,我們通常在一個(gè)靜態(tài)方法中使用Monitor類(lèi)配合一個(gè)Type類(lèi)的實(shí)例。同樣,我們往往會(huì )在一個(gè)非靜態(tài)方法中使用this關(guān)鍵字來(lái)實(shí)現同步。在兩種情況下,我們都是通過(guò)一個(gè)在類(lèi)外部可見(jiàn)的對象對自身進(jìn)行同步。如果其他部分的代碼也利用這些對象來(lái)實(shí)現自身的同步,就會(huì )出現問(wèn)題。為了避免這種潛在的問(wèn)題,我們推薦使用一個(gè)類(lèi)型為object的名為SyncRoot的私有成員,至于該成員是靜態(tài)的還是非靜態(tài)的則由需要而定。

  例5-8

  System.Collections.ICollection接口提供了object類(lèi)型的SyncRoot{get;}屬性。大多數的集合類(lèi)(泛型或非泛型)都實(shí)現了該接口。同樣地,可以使用該屬性同步對集合中元素的訪(fǎng)問(wèn)。不過(guò)在這里SyncRoot模式并沒(méi)有被真正的應用,因為我們對訪(fǎng)問(wèn)進(jìn)行同步所使用對象不是私有的。

  例5-9

  5.6.4 線(xiàn)程安全類(lèi)

  若一個(gè)類(lèi)的每個(gè)實(shí)例在同一時(shí)間不能被一個(gè)以上的線(xiàn)程所訪(fǎng)問(wèn),則該類(lèi)稱(chēng)之為一個(gè)線(xiàn)程安全的類(lèi)。為了創(chuàng )建一個(gè)線(xiàn)程安全的類(lèi),只需將我們見(jiàn)過(guò)的SyncRoot模式應用于它所包含的方法。如果一個(gè)類(lèi)想變成線(xiàn)程安全的,而又不想為類(lèi)中代碼增加過(guò)多負擔,那么有一個(gè)好方法就是像下面這樣為其提供一個(gè)經(jīng)過(guò)線(xiàn)程安全包裝的繼承類(lèi)。

  例5-10

  另一種方法就是使用System.Runtime.Remoting.Contexts.SynchronizationAttribute,這點(diǎn)我們將在本章稍后討論。

  5.6.5 Monitor.TryEnter()方法

  該方法與Enter()相似,只不過(guò)它是非阻塞的。如果資源的獨占訪(fǎng)問(wèn)權已經(jīng)被另一個(gè)線(xiàn)程占據,該方法將立即返回一個(gè)false返回值。我們也可以調用TryEnter()方法,讓它以毫秒為單位阻塞一段有限的時(shí)間。因為該方法的返回結果并不確定,并且當獲得獨占訪(fǎng)問(wèn)權后必須在finally子句中釋放該權力,所以建議當TryEnter()失敗時(shí)立即退出正在調用的函數:

  例5-11[2]

  5.6.6 Monitor類(lèi)的Wait()方法, Pulse()方法以及PulseAll()方法

  Wait()、Pulse()與PulseAll()方法必須在一起使用并且需要結合一個(gè)小場(chǎng)景才能被正確理解。我們的想法是這樣的:一個(gè)線(xiàn)程獲得了某個(gè)對象的獨占訪(fǎng)問(wèn)權,而它決定等待(通過(guò)調用Wait())直到該對象的狀態(tài)發(fā)生變化。為此,該線(xiàn)程必須暫時(shí)失去對象獨占訪(fǎng)問(wèn)權,以便讓另一個(gè)線(xiàn)程修改對象的狀態(tài)。修改對象狀態(tài)的線(xiàn)程必須使用Pulse()方法通知那個(gè)等待線(xiàn)程修改完成。下面有一個(gè)小場(chǎng)景具體說(shuō)明了這一情況。

  擁有OBJ對象獨占訪(fǎng)問(wèn)權的T1線(xiàn)程,調用Wait(OBJ)方法將它自己注冊到OBJ對象的被動(dòng)等待列表中。

  由于以上的調用,T1失去了對OBJ的獨占訪(fǎng)問(wèn)權。因此,另一個(gè)線(xiàn)程T2通過(guò)調用Enter(OBJ)獲得OBJ的獨占訪(fǎng)問(wèn)權。

  T2最終修改了OBJ的狀態(tài)并調用Pulse(OBJ)通知了這次修改。該調用將導致OBJ被動(dòng)等待列表中的第一個(gè)線(xiàn)程(在這里是T1)被移到OBJ的主動(dòng)等待列表的首位。而一旦OBJ的獨占訪(fǎng)問(wèn)權被釋放,OBJ主動(dòng)等待列表中的第一個(gè)線(xiàn)程將被確保獲得該權力。然后它就從Wait(OBJ)方法中退出等待狀態(tài)。

  在我們的場(chǎng)景中,T2調用Exit(OBJ)以釋放對OBJ的獨占訪(fǎng)問(wèn)權,接著(zhù)T1恢復訪(fǎng)問(wèn)權并從Wait(OBJ)方法中退出。

  PulseAll()將使得被動(dòng)等待列表中的線(xiàn)程全部轉移到主動(dòng)等待列表中。注意這些線(xiàn)程將按照它們調用Wait()的順序到達非阻塞態(tài)。

  如果Wait(OBJ)被一個(gè)調用了多次Enter(OBJ)的線(xiàn)程所調用,那么該線(xiàn)程將需要調用相同次數的Exit(OBJ)以釋放對OBJ的訪(fǎng)問(wèn)權。即使在這種情況下,另一個(gè)線(xiàn)程調用一次Pulse(OBJ)就足以將第一個(gè)線(xiàn)程變成非阻塞態(tài)。

  下面的程序通過(guò)ping與pong兩個(gè)線(xiàn)程以交替的方式使用一個(gè)ball對象的訪(fǎng)問(wèn)權來(lái)演示該功能。

  例5-12

  該程序輸出(以不確定的方式):

  pong線(xiàn)程沒(méi)有結束并且仍然阻塞在Wait()方法上。由于pong線(xiàn)程是第二個(gè)獲得ball對象的獨占訪(fǎng)問(wèn)權的,所以才導致了該結果。

【進(jìn)程和線(xiàn)程的區別】相關(guān)文章:

設計并發(fā)服務(wù)器,使用多進(jìn)程與多線(xiàn)程有什么區別?12-03

關(guān)于linux查看進(jìn)程ps top區別09-30

Linux多線(xiàn)程:線(xiàn)程的分離與結合08-17

ssat和托福的區別10-14

咖啡的種類(lèi)和區別10-09

速錄和速記的區別10-01

綠茶和白茶的區別10-22

打工和創(chuàng )業(yè)的區別11-02

紋眉和畫(huà)眉的區別07-19

朗誦和播音的區別09-04

激情欧美日韩一区二区,浪货撅高贱屁股求主人调教视频,精品无码成人片一区二区98,国产高清av在线播放,色翁荡息又大又硬又粗视频