




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1/1C_多線程的詳細(xì)教程C_多線程的詳細(xì)教程Windows是一個(gè)多任務(wù)的系統(tǒng),如果你使用的是windows2000及其以上版本,你可以通過(guò)任務(wù)管理器查看當(dāng)前系統(tǒng)運(yùn)行的程序和進(jìn)程。
什么是進(jìn)程呢?當(dāng)一個(gè)程序開始運(yùn)行時(shí),它就是一個(gè)進(jìn)程,進(jìn)程所指包括運(yùn)行中的程序和程序所使用到的內(nèi)存和系統(tǒng)資源。
而一個(gè)進(jìn)程又是由多個(gè)線程所組成的,線程是程序中的一個(gè)執(zhí)行流,每個(gè)線程都有自己的專有寄存器(棧指針、程序計(jì)數(shù)器等),但代碼區(qū)是共享的,即不同的線程可以執(zhí)行同樣的函數(shù)。
多線程是指程序中包含多個(gè)執(zhí)行流,即在一個(gè)程序中可以同時(shí)運(yùn)行多個(gè)不同的線程來(lái)執(zhí)行不同的任務(wù),也就是說(shuō)允許單個(gè)程序創(chuàng)建多個(gè)并行執(zhí)行的線程來(lái)完成各自的任務(wù)。
瀏覽器就是一個(gè)很好的多線程的例子,在瀏覽器中你可以在下載JAVA小應(yīng)用程序或圖象的同時(shí)滾動(dòng)頁(yè)面,在訪問(wèn)新頁(yè)面時(shí),播放動(dòng)畫和聲音,打印文件等。
多線程的好處在于可以提高CPU的利用率任何一個(gè)程序員都不希望自己的程序很多時(shí)候沒事可干,在多線程程序中,一個(gè)線程必須等待的時(shí)候,CPU可以運(yùn)行其它的線程而不是等待,這樣就大大提高了程序的效率。
然而我們也必須認(rèn)識(shí)到線程本身可能影響系統(tǒng)性能的不利方面,以正確使用線程:
線程也是程序,所以線程需要占用內(nèi)存,線程越多占用內(nèi)存也越多多線程需要協(xié)調(diào)和管理,所以需要CPU時(shí)間跟蹤線程線程之間對(duì)共享資源的訪問(wèn)會(huì)相互影響,必須解決競(jìng)用共享資源的問(wèn)題線程太多會(huì)導(dǎo)致控制太復(fù)雜,最終可能造成很多Bug基于以上認(rèn)識(shí),我們可以一個(gè)比喻來(lái)加深理解。
假設(shè)有一個(gè)公司,公司里有很多各司其職的職員,那么我們可以認(rèn)為這個(gè)正常運(yùn)作的公司就是一個(gè)進(jìn)程,而公司里的職員就是線程。
一個(gè)公司至少得有一個(gè)職員吧,同理,一個(gè)進(jìn)程至少包含一個(gè)線程。
在公司里,你可以一個(gè)職員干所有的事,但是效率很顯然是高不起來(lái)的,一個(gè)人的公司也不可能做大;一個(gè)程序中也可以只用一個(gè)線程去做事,事實(shí)上,一些過(guò)時(shí)的語(yǔ)言如fortune,basic都是如此,但是象一個(gè)人的公司一樣,效率很低,如果做大程序,效率更低事實(shí)上現(xiàn)在幾乎沒有單線程的商業(yè)軟件。
公司的職員越多,老板就得發(fā)越多的薪水給他們,還得耗費(fèi)大量精力去管理他們,協(xié)調(diào)他們之間的矛盾和利益;程序也是如此,線程越多耗費(fèi)的資源也越多,需要CPU時(shí)間去跟蹤線程,還得解決諸如死鎖,同步等問(wèn)題。
總之,如果你不想你的公司被稱為皮包公司,你就得多幾個(gè)員工;如果你不想讓你的程序顯得稚氣,就在你的程序里引入多線程吧!本文將對(duì)C#編程中的多線程機(jī)制進(jìn)行探討,通過(guò)一些實(shí)例解決對(duì)線程的控制,多線程間通訊等問(wèn)題。
為了省去創(chuàng)建GUI那些繁瑣的步驟,更清晰地逼近線程的本質(zhì),下面所有的程序都是控制臺(tái)程序,程序最后的Console.ReadLine()是為了使程序中途停下來(lái),以便看清楚執(zhí)行過(guò)程中的輸出。
好了,廢話少說(shuō),讓我們來(lái)體驗(yàn)一下多線程的C#吧!二.操縱一個(gè)線程任何程序在執(zhí)行時(shí),至少有一個(gè)主線程,下面這段小程序可以給讀者一個(gè)直觀的印象:
//SystemThread.csusingSystem;usingSystem.Threading;namespaceThreadTest{classRunIt{[STAThread]staticvoidMain(string[]args){Thread.CurrentThread.Name=SystemThread;//給當(dāng)前線程起名為SystemThreadConsole.WriteLine(Thread.CurrentThread.Name+’Status:+Thread.CurrentThread.ThreadState);Console.ReadLine();}}}編譯執(zhí)行后你看到了什么?是的,程序?qū)a(chǎn)生如下輸出:
SystemThread’sStatus:Running在這里,我們通過(guò)Thread類的靜態(tài)屬性CurrentThread獲取了當(dāng)前執(zhí)行的線程,對(duì)其Name屬性賦值SystemThread,最后還輸出了它的當(dāng)前狀態(tài)(ThreadState)。
所謂靜態(tài)屬性,就是這個(gè)類所有對(duì)象所公有的屬性,不管你創(chuàng)建了多少個(gè)這個(gè)類的實(shí)例,但是類的靜態(tài)屬性在內(nèi)存中只有一個(gè)。
很容易理解CurrentThread為什么是靜態(tài)的雖然有多個(gè)線程同時(shí)存在,但是在某一個(gè)時(shí)刻,CPU只能執(zhí)行其中一個(gè)。
就像上面程序所演示的,我們通過(guò)Thread類來(lái)創(chuàng)建和控制線程。
注意到程序的頭部,我們使用了如下命名空間:
usingSystem;usingSystem.Threading;在.netframeworkclasslibrary中,所有與多線程機(jī)制應(yīng)用相關(guān)的類都是放在System.Threading命名空間中的。
其中提供Thread類用于創(chuàng)建線程,ThreadPool類用于管理線程池等等,此外還提供解決了線程執(zhí)行安排,死鎖,線程間通訊等實(shí)際問(wèn)題的機(jī)制。
如果你想在你的應(yīng)用程序中使用多線程,就必須包含這個(gè)類。
Thread類有幾個(gè)至關(guān)重要的方法,描述如下:
Start():啟動(dòng)線程Sleep(int):靜態(tài)方法,暫停當(dāng)前線程指定的毫秒數(shù)Abort():通常使用該方法來(lái)終止一個(gè)線程Suspend():
該方法并不終止未完成的線程,它僅僅掛起線程,以后還可恢復(fù)。
Resume():恢復(fù)被Suspend()方法掛起的線程的執(zhí)行下面我們就動(dòng)手來(lái)創(chuàng)建一個(gè)線程,使用Thread類創(chuàng)建線程時(shí),只需提供線程入口即可。
線程入口使程序知道該讓這個(gè)線程干什么事,在C#中,線程入口是通過(guò)ThreadStart代理(delegate)來(lái)提供的,你可以把ThreadStart理解為一個(gè)函數(shù)指針,指向線程要執(zhí)行的函數(shù),當(dāng)調(diào)用Thread.Start()方法后,線程就開始執(zhí)行ThreadStart所代表或者說(shuō)指向的函數(shù)。
打開你的VS.net,新建一個(gè)控制臺(tái)應(yīng)用程序(ConsoleApplication),下面這些代碼將讓你體味到完全控制一個(gè)線程的無(wú)窮樂(lè)趣!//ThreadTest.csusingSystem;usingSystem.Threading;namespaceThreadTest{publicclassAlpha{publicvoidBeta(){while(true){Console.WriteLine(Alpha.Betaisrunninginitsownthread.);}}};publicclassSimple{publicstaticintMain(){Console.WriteLine(ThreadStart/Stop/JoinSample);AlphaoAlpha=newAlpha();file://這里創(chuàng)建一個(gè)線程,使之執(zhí)行Alpha類的Beta()方法ThreadoThread=newThread(newThreadStart(oAlpha.Beta));oThread.Start();while(!oThread.IsAlive);Thread.Sleep(1);oThread.Abort();oThread.Join();Console.WriteLine();Console.WriteLine(Alpha.Betahasfinished);try{Console.WriteLine(TrytorestarttheAlpha.Betathread);oThread.Start();}catch(ThreadStateException){Console.Write(ThreadStateExceptiontryingtorestartAlpha.Beta.);Console.WriteLine(Expectedsinceabortedthreadscannotberestarted.);Console.ReadLine();}return0;}}}這段程序包含兩個(gè)類Alpha和Simple,在創(chuàng)建線程oThread時(shí)我們用指向Alpha.Beta()方法的初始化了ThreadStart代理(delegate)對(duì)象,當(dāng)我們創(chuàng)建的線程oThread調(diào)用oThread.Start()方法啟動(dòng)時(shí),實(shí)際上程序運(yùn)行的是Alpha.Beta()方法:
AlphaoAlpha=newAlpha();ThreadoThread=newThread(newThreadStart(oAlpha.Beta));oThread.Start();然后在Main()函數(shù)的while循環(huán)中,我們使用靜態(tài)方法Thread.Sleep()讓主線程停了1ms,這段時(shí)間CPU轉(zhuǎn)向執(zhí)行線程oThread。
然后我們?cè)噲D用Thread.Abort()方法終止線程oThread,注意后面的oThread.Join(),Thread.Join()方法使主線程等待,直到oThread線程結(jié)束。
你可以給Thread.Join()方法指定一個(gè)int型的參數(shù)作為等待的最長(zhǎng)時(shí)間。
之后,我們?cè)噲D用Thread.Start()方法重新啟動(dòng)線程oThread,但是顯然Abort()方法帶來(lái)的后果是不可恢復(fù)的終止線程,所以最后程序會(huì)拋出ThreadStateException異常。
程序最后得到的結(jié)果將如下圖:
在這里我們要注意的是其它線程都是依附于Main()函數(shù)所在的線程的,Main()函數(shù)是C#程序的入口,起始線程可以稱之為主線程,如果所有的前臺(tái)線程都停止了,那么主線程可以終止,而所有的后臺(tái)線程都將無(wú)條件終止。
而所有的線程雖然在微觀上是串行執(zhí)行的,但是在宏觀上你完全可以認(rèn)為它們?cè)诓⑿袌?zhí)行。
#endregion讀者一定注意到了Thread.ThreadState這個(gè)屬性,這個(gè)屬性代表了線程運(yùn)行時(shí)狀態(tài),在不同的情況下有不同的值,于是我們有時(shí)候可以通過(guò)對(duì)該值的判斷來(lái)設(shè)計(jì)程序流程。
ThreadState在各種情況下的可能取值如下:
Aborted:
線程已停止AbortRequested:
線程的Thread.Abort()方法已被調(diào)用,但是線程還未停止Background:
線程在后臺(tái)執(zhí)行,與屬性Thread.IsBackground有關(guān)Running:
線程正在正常運(yùn)行Stopped:
線程已經(jīng)被停止StopRequested:
線程正在被要求停止Suspended:
線程已經(jīng)被掛起(此狀態(tài)下,可以通過(guò)調(diào)用Resume()方法重新運(yùn)行)SuspendRequested:
線程正在要求被掛起,但是未來(lái)得及響應(yīng)Unstarted:
未調(diào)用Thread.Start()開始線程的運(yùn)行WaitSleepJoin:
線程因?yàn)檎{(diào)用了Wait(),Sleep()或Join()等方法處于封鎖狀態(tài)上面提到了Background狀態(tài)表示該線程在后臺(tái)運(yùn)行,那么后臺(tái)運(yùn)行的線程有什么特別的地方呢?其實(shí)后臺(tái)線程跟前臺(tái)線程只有一個(gè)區(qū)別,那就是后臺(tái)線程不妨礙程序的終止。
一旦一個(gè)進(jìn)程所有的前臺(tái)線程都終止后,CLR(通用語(yǔ)言運(yùn)行環(huán)境)將通過(guò)調(diào)用任意一個(gè)存活中的后臺(tái)進(jìn)程的Abort()方法來(lái)徹底終止進(jìn)程。
當(dāng)線程之間爭(zhēng)奪CPU時(shí)間時(shí),CPU按照是線程的優(yōu)先級(jí)給予服務(wù)的。
在C#應(yīng)用程序中,用戶可以設(shè)定5個(gè)不同的優(yōu)先級(jí),由高到低分別是Highest,AboveNormal,Normal,BelowNormal,Lowest,在創(chuàng)建線程時(shí)如果不指定優(yōu)先級(jí),那么系統(tǒng)默認(rèn)為ThreadPriority.Normal。
給一個(gè)線程指定優(yōu)先級(jí),我們可以使用如下代碼:
//設(shè)定優(yōu)先級(jí)為最低myThread.Priority=ThreadPriority.Lowest;通過(guò)設(shè)定線程的優(yōu)先級(jí),我們可以安排一些相對(duì)重要的線程優(yōu)先執(zhí)行,例如對(duì)用戶的響應(yīng)等等。
現(xiàn)在我們對(duì)怎樣創(chuàng)建和控制一個(gè)線程已經(jīng)有了一個(gè)初步的了解,下面我們將深入研究線程實(shí)現(xiàn)中比較典型的的問(wèn)題,并且探討其解決方法。
三.線程的同步和通訊生產(chǎn)者和消費(fèi)者假設(shè)這樣一種情況,兩個(gè)線程同時(shí)維護(hù)一個(gè)隊(duì)列,如果一個(gè)線程對(duì)隊(duì)列中添加元素,而另外一個(gè)線程從隊(duì)列中取用元素,那么我們稱添加元素的線程為生產(chǎn)者,稱取用元素的線程為消費(fèi)者。
生產(chǎn)者與消費(fèi)者問(wèn)題看起來(lái)很簡(jiǎn)單,但是卻是多線程應(yīng)用中一個(gè)必須解決的問(wèn)題,它涉及到線程之間的同步和通訊問(wèn)題。
前面說(shuō)過(guò),每個(gè)線程都有自己的資源,但是代碼區(qū)是共享的,即每個(gè)線程都可以執(zhí)行相同的函數(shù)。
但是多線程環(huán)境下,可能帶來(lái)的問(wèn)題就是幾個(gè)線程同時(shí)執(zhí)行一個(gè)函數(shù),導(dǎo)致數(shù)據(jù)的混亂,產(chǎn)生不可預(yù)料的結(jié)果,因此我們必須避免這種情況的發(fā)生。
C#提供了一個(gè)關(guān)鍵字lock,它可以把一段代碼定義為互斥段(criticalsection),互斥段在一個(gè)時(shí)刻內(nèi)只允許一個(gè)線程進(jìn)入執(zhí)行,而其他線程必須等待。
在C#中,關(guān)鍵字lock定義如下:
lock(expression)statement_blockexpression代表你希望跟蹤的對(duì)象,通常是對(duì)象引用。
一般地,如果你想保護(hù)一個(gè)類的實(shí)例,你可以使用this;如果你希望保護(hù)一個(gè)靜態(tài)變量(如互斥代碼段在一個(gè)靜態(tài)方法內(nèi)部),一般使用類名就可以了。
而statement_block就是互斥段的代碼,這段代碼在一個(gè)時(shí)刻內(nèi)只可能被一個(gè)線程執(zhí)行。
下面是一個(gè)使用lock關(guān)鍵字的典型例子,我將在注釋里向大家說(shuō)明lock關(guān)鍵字的用法和用途:
//lock.csusingSystem;usingSystem.Threading;internalclassAccount{intbalance;Randomr=newRandom();internalAccount(intinitial){balance=initial;}internalintWithdraw(intamount){if(balance0){file://如果balance小于0則拋出異常thrownewException(NegativeBalance);}//下面的代碼保證在當(dāng)前線程修改balance的值完成之前//不會(huì)有其他線程也執(zhí)行這段代碼來(lái)修改balance的值//因此,balance的值是不可能小于0的lock(this){Console.WriteLine(CurrentThread:+Thread.CurrentThread.Name);file://如果沒有l(wèi)ock關(guān)鍵字的保護(hù),那么可能在執(zhí)行完if的條件判斷之后file://另外一個(gè)線程卻執(zhí)行了balance=balance-amount修改了balance的值file://而這個(gè)修改對(duì)這個(gè)線程是不可見的,所以可能導(dǎo)致這時(shí)if的條件已經(jīng)不成立了file://但是,這個(gè)線程卻繼續(xù)執(zhí)行balance=balance-amount,所以導(dǎo)致balance可能小于0if(balance=amount){Thread.Sleep(5);balance=balance-amount;returnamount;}else{return0;//transactionrejected}}}internalvoidDoTransactions(){for(inti=0;i100;i++)Withdraw(r.Next(-50,100));}}internalclassTest{staticinternalThread[]threads=newThread[10];publicstaticvoidMain(){Accountacc=newAccount(0);for(inti=0;i10;i++){Threadt=newThread(newThreadStart(acc.DoTransactions));threads[i]=t;}for(inti=0;i10;i++)threads[i].Name=i.ToString();for(inti=0;i10;i++)threads[i].Start();Console.ReadLine();}}而多線程公用一個(gè)對(duì)象時(shí),也會(huì)出現(xiàn)和公用代碼類似的問(wèn)題,這種問(wèn)題就不應(yīng)該使用lock關(guān)鍵字了,這里需要用到System.Threading中的一個(gè)類Monitor,我們可以稱之為監(jiān)視器,Monitor提供了使線程共享資源的方案。
Monitor類可以鎖定一個(gè)對(duì)象,一個(gè)線程只有得到這把鎖才可以對(duì)該對(duì)象進(jìn)行操作。
對(duì)象鎖機(jī)制保證了在可能引起混亂的情況下一個(gè)時(shí)刻只有一個(gè)線程可以訪問(wèn)這個(gè)對(duì)象。
Monitor必須和一個(gè)具體的對(duì)象相關(guān)聯(lián),但是由于它是一個(gè)靜態(tài)的類,所以不能使用它來(lái)定義對(duì)象,而且它的所有方法都是靜態(tài)的,不能使用對(duì)象來(lái)引用。
下面代碼說(shuō)明了使用Monitor鎖定一個(gè)對(duì)象的情形:
QueueoQueue=newQueue();Monitor.Enter(oQueue);//現(xiàn)在oQueue對(duì)象只能被當(dāng)前線程操縱了Monitor.Exit(oQueue);//釋放鎖如上所示,當(dāng)一個(gè)線程調(diào)用Monitor.Enter()方法鎖定一個(gè)對(duì)象時(shí),這個(gè)對(duì)象就歸它所有了,其它線程想要訪問(wèn)這個(gè)對(duì)象,只有等待它使用
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 深入研究計(jì)算機(jī)二級(jí)考試知識(shí)點(diǎn)試題及答案
- 系統(tǒng)架構(gòu)設(shè)計(jì)師考試中的學(xué)習(xí)歷程與關(guān)鍵時(shí)刻試題及答案
- 圖書管理員考試的應(yīng)用技巧及試題及答案
- 臨床用藥的個(gè)體化管理考試試題及答案
- 老城模擬測(cè)試題及答案
- 線上健康管理服務(wù)的趨勢(shì)試題及答案
- 母豬護(hù)理知識(shí)更新訓(xùn)練試題及答案
- 生鮮客服考試題及答案
- 系統(tǒng)架構(gòu)設(shè)計(jì)師備考時(shí)的資源利用技巧試題及答案
- 副主任考試題及答案
- 第13課 現(xiàn)代戰(zhàn)爭(zhēng)與不同文化的碰撞和交流 課件(17張PPT)
- 生產(chǎn)安全事故應(yīng)急預(yù)案(含深井鑄造)
- CRRT vs 中毒血液凈化模式選擇
- 《等量代換》ppt(基礎(chǔ)教育)
- 高壓液氧泵原理、結(jié)構(gòu)及檢修、結(jié)構(gòu)及檢修
- 結(jié)核病防治知識(shí)培訓(xùn)試題及答案
- 變頻器日常維護(hù)及常見故障處理85頁(yè)P(yáng)PT課件
- XX光伏電站項(xiàng)目方案(自投)
- 《長(zhǎng)方體正方體》課前小研究
- MIL-STD-105E抽樣計(jì)劃表
- 中國(guó)聯(lián)通消防安全管理規(guī)定
評(píng)論
0/150
提交評(píng)論