C#多線程技術(shù)PPT優(yōu)秀課件_第1頁
C#多線程技術(shù)PPT優(yōu)秀課件_第2頁
C#多線程技術(shù)PPT優(yōu)秀課件_第3頁
C#多線程技術(shù)PPT優(yōu)秀課件_第4頁
C#多線程技術(shù)PPT優(yōu)秀課件_第5頁
已閱讀5頁,還剩18頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、1第第9章章 C#多線程技術(shù)多線程技術(shù)9.1線程概述使用C#編寫任何程序時(shí),都有一個(gè)入口:Main()方法。程序從Main()方法的第一條語句開始執(zhí)行,直到這個(gè)方法返回為止。這樣的程序結(jié)構(gòu)非常適合于有一個(gè)可識別的任務(wù)序列的程序,但程序常常需要同時(shí)完成多個(gè)任務(wù)。例如在使用文字處理軟件的時(shí)候,用戶在輸入文字的同時(shí),軟件能同步進(jìn)行拼寫檢查而不需要用戶的等待;再如在一個(gè)應(yīng)用程序的打印功能中,如果程序只能執(zhí)行一個(gè)任務(wù)序列,用戶可能需要等待所有的打印任務(wù)完成后才能繼續(xù)操作,這時(shí)就需要能讓程序同時(shí)處理多個(gè)任務(wù)的能力。在C#應(yīng)用程序中,第一個(gè)線程總是Main()方法,因?yàn)榈谝粋€(gè)線程是由.NET運(yùn)行庫開始執(zhí)行的

2、,Main()方法是.NET運(yùn)行庫選擇的第一個(gè)方法。后續(xù)的線程由應(yīng)用程序在內(nèi)部啟動(dòng),即應(yīng)用程序可以創(chuàng)建和啟動(dòng)新的線程。 29.2 .NET對多線程的支持對多線程的支持 在.NET程序設(shè)計(jì)中,線程是使用Thread類來處理的,該類在System.Threading命名空間中。一個(gè)Thread實(shí)例管理一個(gè)線程,即執(zhí)行序列。通過簡單實(shí)例化一個(gè)Thread對象,就可以創(chuàng)建一個(gè)線程,然后通過Thread對象提供的方法對線程進(jìn)行管理。 9.2.1 線程的建立與啟動(dòng)線程的建立與啟動(dòng)假定我們需要編寫一個(gè)文件壓縮軟件,用戶點(diǎn)擊壓縮按鈕后開始壓縮指定的文件。因?yàn)檎麄€(gè)壓縮過程需要一定的時(shí)間才能完成,而用戶此時(shí)還可能

3、需要移動(dòng)或縮放程序的窗口,甚至?xí)和;蛑兄巩?dāng)前文件的壓縮。此時(shí)一般需要?jiǎng)?chuàng)建一個(gè)單獨(dú)的線程來處理這個(gè)壓縮過程使得在壓縮過程中可以不中斷用戶界面的響應(yīng)。因此,我們需要實(shí)例化一個(gè)Thread對象來創(chuàng)建這個(gè)線程:/假設(shè)DoCompress是前面已經(jīng)聲明了的一個(gè)ThreadStart委托Thread compressThread = New Thread(entryPoint);這段代碼指定線程對象的實(shí)例名為compressThread。在一個(gè)應(yīng)用程序中創(chuàng)建另一個(gè)線程,執(zhí)行一些任務(wù),通常稱為工作線程(worker thread),這里compressThread就是一個(gè)工作線程,而Main()方法所在的線

4、程常被稱為主線程。39.2.1 線程的建立與啟動(dòng)線程的建立與啟動(dòng)從代碼可以看出,Thread構(gòu)造函數(shù)需要一個(gè)參數(shù),用于指定線程的入口即線程開始執(zhí)行的方法,因?yàn)槲覀儌魉偷氖欠椒ǖ脑敿?xì)信息,所以需要使用委托。實(shí)際上,該委托已經(jīng)在System.Threading命名空間中定義好了。它稱為ThreadStart,其聲明如下所示: public delegate void ThreadStart();傳送給構(gòu)造函數(shù)的參數(shù)必須是這種類型的委托。上面的例子中是entryPoint,我們來看如何定義這個(gè)委托:/ 實(shí)際線程執(zhí)行的方法static void DoCompress() / 壓縮代碼ThreadSta

5、rt entryPoint = new ThreadStart(DoCompress);線程對象建立完成后,新線程實(shí)際上并沒有執(zhí)行任務(wù),它只是在等待執(zhí)行。我們需要顯式地調(diào)用Thread對象的Start()方法來啟動(dòng)線程:compressThread.Start();此外還可以使用Thread對象的Name屬性給線程賦予一個(gè)友好的名稱。49.2.2 線程的掛起、恢復(fù)與終止線程的掛起、恢復(fù)與終止啟動(dòng)了一個(gè)線程后,線程將運(yùn)行到所在的方法結(jié)束為止,在此期間還可以掛起、恢復(fù)或中止它。掛起一個(gè)線程就是讓它進(jìn)入睡眠狀態(tài),此時(shí),線程僅是停止運(yùn)行某段時(shí)間,不占用任何處理器時(shí)間,以后還可以恢復(fù),從被掛起的那個(gè)狀態(tài)

6、重新運(yùn)行。如果線程被中止,就是停止運(yùn)行,Windows會(huì)永久地刪除該線程的所有數(shù)據(jù),所以該線程不能重新啟動(dòng)。 繼續(xù)上面的文件壓縮例子,假定由于某些原因,用戶界面線程顯示一個(gè)對話框,允許用戶選擇臨時(shí)暫停壓縮過程。在主線程中編寫如下響應(yīng):compressThread.Suspend(); 如果用戶以后要求恢復(fù)該線程,可以使用下面的方法:CompressThread.Resume() 最后,如果用戶決定不需要繼續(xù)壓縮的話,單擊取消按鈕,可以使用下面的方法:CompressThread.Abort()59.4 線程的優(yōu)先級線程的優(yōu)先級如果在應(yīng)用程序中有多個(gè)線程在運(yùn)行,但一些線程比另一些線程重要因而需要

7、分配更多的CPU時(shí)間該怎么辦?在這種情況下,可以在一個(gè)進(jìn)程中為不同的線程指定不同的優(yōu)先級。一般情況下,如果有優(yōu)先級較高的線程在工作,就不會(huì)給優(yōu)先級較低的線程分配任何時(shí)間片,其優(yōu)點(diǎn)是可以保證給接收用戶輸入的線程指定較高的優(yōu)先級。在大多數(shù)的時(shí)間內(nèi),這個(gè)線程什么也不做,而其他線程則執(zhí)行它們的任務(wù)。但是,如果用戶輸入了信息,這個(gè)線程就立即獲得比應(yīng)用程序中其他線程更高的優(yōu)先級,在短時(shí)間內(nèi)處理用戶輸入控件。線程的優(yōu)先級定義為ThreadPriority枚舉類型,取值如表9.1所示:表表9.1 線程的優(yōu)先級及其含義線程的優(yōu)先級及其含義69.4 線程的優(yōu)先級線程的優(yōu)先級高優(yōu)先級的線程可以完全阻止低優(yōu)先級的線程

8、執(zhí)行,因此在改變線程的優(yōu)先級時(shí)要特別小心以免造成某些線程得不到CPU時(shí)間。此外,每個(gè)進(jìn)程都有個(gè)基本優(yōu)先級,這些值與進(jìn)程的優(yōu)先級是有關(guān)系的。給線程指定較高的優(yōu)先級,可以確保它在該進(jìn)程內(nèi)比其他線程優(yōu)先執(zhí)行,但系統(tǒng)上可能還運(yùn)行著其他進(jìn)程,它們的線程有更高的優(yōu)先級。如Windows給自己的操作系統(tǒng)線程指定高優(yōu)先級。在【例9.1】中,對Main()方法做如下修改,就可以看出修改線程的優(yōu)先級的效果:/ 建立新線程對象ThreadStart workerStart = new ThreadStart(DisplayNumbers);Thread workerThread = new Thread(worke

9、rStart);workerThread.Name = Worker Thread;workerThread.Priority = AboveNormal;79.4 線程的優(yōu)先級線程的優(yōu)先級其中通過代碼設(shè)置工作線程的優(yōu)先級比主線程高,運(yùn)行結(jié)果如下所示:請輸入一個(gè)數(shù)字:1000000線程: Main Thread 已開始運(yùn)行.Main Thread: 當(dāng)前計(jì)數(shù)為 1000000Main Thread: 當(dāng)前計(jì)數(shù)為 2000000Main Thread: 當(dāng)前計(jì)數(shù)為 3000000Main Thread: 當(dāng)前計(jì)數(shù)為 4000000Main Thread: 當(dāng)前計(jì)數(shù)為 5000000Main Th

10、read: 當(dāng)前計(jì)數(shù)為 6000000線程: Worker Thread 已開始運(yùn)行.Worker Thread: 當(dāng)前計(jì)數(shù)為 1000000Worker Thread: 當(dāng)前計(jì)數(shù)為 2000000Worker Thread: 當(dāng)前計(jì)數(shù)為 300000089.4 線程的優(yōu)先級線程的優(yōu)先級Worker Thread: 當(dāng)前計(jì)數(shù)為 4000000Worker Thread: 當(dāng)前計(jì)數(shù)為 5000000Worker Thread: 當(dāng)前計(jì)數(shù)為 6000000Worker Thread: 當(dāng)前計(jì)數(shù)為 7000000Worker Thread: 當(dāng)前計(jì)數(shù)為 8000000線程 Worker Thread

11、 完成.Main Thread: 當(dāng)前計(jì)數(shù)為 7000000Main Thread: 當(dāng)前計(jì)數(shù)為 8000000線程 Main Thread 完成.這說明,當(dāng)工作線程的優(yōu)先級為AboveNormal時(shí),一旦工作線程被啟動(dòng),主線程就不再運(yùn)行,直到工作線程結(jié)束后主線程才重新計(jì)算。讓我們繼續(xù)試驗(yàn)操作系統(tǒng)如何對線程分配CPU時(shí)間:99.4 線程的優(yōu)先級線程的優(yōu)先級在DisplayNumbers()方法的循環(huán)體中加上一句代碼,:if(i%interval = 0)Console.WriteLine(thisThread.Name + : 當(dāng)前計(jì)數(shù)為 + i);Thread.Sleep(10);/ 讓當(dāng)前

12、工作線程暫停10毫秒現(xiàn)在來看運(yùn)行結(jié)果:請輸入一個(gè)數(shù)字:1000000線程: Main Thread 已開始運(yùn)行.Main Thread: 當(dāng)前計(jì)數(shù)為 1000000線程: Worker Thread 已開始運(yùn)行.Worker Thread: 當(dāng)前計(jì)數(shù)為 1000000Main Thread: 當(dāng)前計(jì)數(shù)為 2000000Main Thread: 當(dāng)前計(jì)數(shù)為 3000000Worker Thread: 當(dāng)前計(jì)數(shù)為 2000000Main Thread: 當(dāng)前計(jì)數(shù)為 4000000Worker Thread: 當(dāng)前計(jì)數(shù)為 3000000Worker Thread: 當(dāng)前計(jì)數(shù)為 4000000109.

13、4 線程的優(yōu)先級線程的優(yōu)先級Main Thread: 當(dāng)前計(jì)數(shù)為 5000000Worker Thread: 當(dāng)前計(jì)數(shù)為 5000000Worker Thread: 當(dāng)前計(jì)數(shù)為 6000000Main Thread: 當(dāng)前計(jì)數(shù)為 6000000Worker Thread: 當(dāng)前計(jì)數(shù)為 7000000Worker Thread: 當(dāng)前計(jì)數(shù)為 8000000線程 Worker Thread 完成.Main Thread: 當(dāng)前計(jì)數(shù)為 7000000Main Thread: 當(dāng)前計(jì)數(shù)為 8000000線程 Main Thread 完成.此時(shí)的結(jié)果與前面有很大的不同,雖然工作線程仍然早于主線程完成,但

14、是在工作線程的計(jì)算過程中,主線程也獲到了CPU時(shí)間。這是因?yàn)樵贒isplayNumbers()方法中使用的Thread靜態(tài)方法Sleep()放棄了CPU時(shí)間,即使當(dāng)前線程具有較高的優(yōu)先級,操作系統(tǒng)也會(huì)把時(shí)間片分配給其他優(yōu)先級低的線程。如果我們把Sleep()的參數(shù)加到100毫秒,運(yùn)行結(jié)果又會(huì)有很大的不同,甚至可能兩個(gè)線程是幾乎并行完成的。119.5 線程同步線程同步使用線程的一個(gè)重要方面是同步訪問多個(gè)線程訪問的任何變量。所謂同步,是指在某一時(shí)刻只有一個(gè)線程可以訪問變量。如果不能確保對變量的訪問是同步的,就可能會(huì)產(chǎn)生錯(cuò)誤或不可預(yù)料的結(jié)果。一般情況下,當(dāng)一個(gè)線程寫入一個(gè)變量,同時(shí)有其他線程讀取或?qū)?/p>

15、入這個(gè)變量時(shí),就應(yīng)同步變量。本節(jié)將簡要介紹同步的一些主要內(nèi)容。9.5.1 同步的含義同步的含義同步問題的產(chǎn)生,主要是由于在高級語言的源代碼中,大多數(shù)情況下看起來是一條語句,但在最后編譯好的匯編語言機(jī)器碼中則會(huì)被翻譯為許多條語句,從而在操作系統(tǒng)調(diào)度時(shí)被劃分到不同的時(shí)間片中。129.5.1 同步的含義同步的含義看看下面這個(gè)語句,假設(shè)message是一個(gè)string對象,已經(jīng)保存了一個(gè)字符串:message += Hello world!;這條語句在C#語法上是一條語句,但在執(zhí)行代碼時(shí),實(shí)際上它涉及到許多操作。需要重新分配內(nèi)存以存儲(chǔ)更長的新字符串,需要設(shè)置變量message使之指向新的內(nèi)存,需要復(fù)制

16、實(shí)際文本等。顯然,這里選擇了一種復(fù)雜字符串,但即使在基本數(shù)字類型上執(zhí)行算術(shù)操作,后臺(tái)進(jìn)行的操作也比從C#代碼中看到的要多。而且,許多操作不能直接在存儲(chǔ)于內(nèi)存空間中的變量上進(jìn)行,它們的值必須單獨(dú)復(fù)制到處理器的特定位置上,即寄存器。只要一個(gè)C#語句翻譯為多個(gè)本機(jī)代碼命令,線程的時(shí)間片就有可能在執(zhí)行該語句的進(jìn)程中終止,如果是這樣,同一個(gè)進(jìn)程中的另一個(gè)線程就會(huì)獲得一個(gè)時(shí)間片,如果涉及到這條語句的變量訪問(在上面的示例中,是message)不是同步的,那么另一個(gè)線程可能讀寫同一個(gè)變量。 139.5.2 在在C#中處理同步中處理同步通過對指定對象的加鎖和解鎖可以同步代碼段的訪問。在.NET的System.

17、Threading命名空間中提供了Monitor類來實(shí)現(xiàn)加鎖與解鎖。這個(gè)類中的方法都是靜態(tài)的,所以不需要實(shí)例化這個(gè)類。表9.2中一些靜態(tài)的方法提供了一種機(jī)制用來同步對象的訪問從而避免死鎖和維護(hù)數(shù)據(jù)的一致性。 表表9.2 Monitor類的主要方法類的主要方法149.5.2 在在C#中處理同步中處理同步以下是使用Monitor類的簡單例子:public void some_method()/ 獲取鎖Monitor.Enter(this); / 處理需要同步的代碼. / 釋放鎖 Monitor.Exit(this); 上面的代碼運(yùn)行可能會(huì)產(chǎn)生問題。當(dāng)代碼運(yùn)行到獲取鎖與釋放鎖之間時(shí)一旦發(fā)生異常,Mo

18、nitor.Exit將不會(huì)返回。這段程序?qū)炱?,其他的線程也將得不到鎖。解決方法是:將代碼放入tryfinally內(nèi),在finally調(diào)用Monitor.Exit,這樣的話最后一定會(huì)釋放鎖。 159.5.2 在在C#中處理同步中處理同步C# lock關(guān)鍵字提供了與Monitoy.Enter和Monitoy.Exit同樣的功能,這種方法用在你的代碼段不能被其他獨(dú)立的線程中斷的情況。通過對Monitor類的簡易封裝,lock為同步訪問變量提供了一個(gè)非常簡單的方式,其用法如下:lock(x)/ 使用x的語句lock語句把變量放在圓括號中,以包裝對象,稱為獨(dú)占鎖或排它鎖。當(dāng)執(zhí)行帶有l(wèi)ock關(guān)鍵字的復(fù)合

19、語句時(shí),獨(dú)占鎖會(huì)保留下來。當(dāng)變量被包裝在獨(dú)占鎖中時(shí),其他線程就不能訪問該變量。如果在上面的代碼中使用獨(dú)占鎖,在執(zhí)行復(fù)合語句時(shí),這個(gè)線程就會(huì)失去其時(shí)間片。如果下一個(gè)獲得時(shí)間片的線程試圖訪問變量,就會(huì)被拒絕。Windows會(huì)讓其他線程處于睡眠狀態(tài),直到解除了獨(dú)占鎖為止。169.5.2 在在C#中處理同步中處理同步【例【例9.2】使用lock同步線程。本示例建立了10個(gè)線程using System;using System.Threading;/銀行帳戶類class Account int balance;/余額Random r = new Random();public Account(int i

20、nitial) balance = initial;/ 取錢int Withdraw(int amount) if (balance = amount) Console.WriteLine(原有余額: + balance);Console.WriteLine(支取金額: - + amount); balance = balance - amount;Console.WriteLine(現(xiàn)有余額: + balance);return amount;else return 0;/ 拒絕交易18【例【例9.2】 / 測試交易public void DoTransactions() / 支取隨機(jī)的金額

21、100次for (int i = 0; i 100; i+) Withdraw(r.Next(1, 100);class TestApppublic static void Main() /建立10個(gè)線程同時(shí)進(jìn)行交易Thread threads = new Thread10;Account acc = new Account (1000);for (int i = 0; i 10; i+) 19【例【例9.2】 Thread t = new Thread(new ThreadStart(acc.DoTransactions);threadsi = t;for (int i = 0; i 10;

22、 i+) threadsi.Start();在這個(gè)示例中,10個(gè)線程同時(shí)進(jìn)行交易,如果不加控制,很可能發(fā)生在支取金額時(shí)對balance字段的訪問沖突。假設(shè)當(dāng)前余額為100,有兩個(gè)線程都要支取60,則各自檢查余額時(shí)都認(rèn)為可以支取,造成的后果則是總共被支取120,從而導(dǎo)致余額為負(fù)值。讀者可以試著將lock語句注釋掉再運(yùn)行,此時(shí)將產(chǎn)生余額為負(fù)的異常。 209.5.3 同步時(shí)要注意的問題同步時(shí)要注意的問題同步線程在多線程應(yīng)用程序中非常重要。但是,這是一個(gè)需要詳細(xì)討論的內(nèi)容,因?yàn)楹苋菀壮霈F(xiàn)微妙且難以察覺的問題,特別是死鎖。線程同步非常重要,但只在需要時(shí)使用也是非常重要的。因?yàn)檫@會(huì)降低性能。原因有兩個(gè):首

23、先,在對象上放置和解開鎖會(huì)帶來某些系統(tǒng)開銷,但這些系統(tǒng)開銷都非常小。第二個(gè)原因更為重要,線程同步使用得越多,等待釋放對象的線程就越多。如果一個(gè)線程在對象上放置了一個(gè)鎖,需要訪問該對象的其他線程就只能暫停執(zhí)行,直到該鎖被解開,才能繼續(xù)執(zhí)行。因此,在lock塊內(nèi)部編寫的代碼越少越好,以免出現(xiàn)線程同步錯(cuò)誤。lock語句在某種意義上就是臨時(shí)禁用應(yīng)用程序的多線程功能,也就臨時(shí)刪除了多線程的各種優(yōu)勢。另一方面,使用過多的同步線程的危險(xiǎn)性(性能和響應(yīng)降低)并沒有在需要時(shí)不使用同步線程那么高(難以跟蹤的運(yùn)行時(shí)錯(cuò)誤)。219.5.3 同步時(shí)要注意的問題同步時(shí)要注意的問題死鎖是一個(gè)錯(cuò)誤,在兩個(gè)線程都需要訪問被互鎖的資源時(shí)發(fā)生。假定一個(gè)線程運(yùn)行下述代碼,其中a和b是兩個(gè)線程都可以訪問的對象引用:lock(a)(lock(b)/ do something同時(shí),另一個(gè)線程運(yùn)行下述代碼:lock(b)(lock(a)/ do something229.5.3

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論