VC#中的多線程編程_第1頁
VC#中的多線程編程_第2頁
VC#中的多線程編程_第3頁
VC#中的多線程編程_第4頁
VC#中的多線程編程_第5頁
已閱讀5頁,還剩5頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

1、Visual C#中的多線程編程C#是.Net平臺的通用開發(fā)工具,它能夠建造所有的.Net應(yīng)用。在.Net中所有線程都運(yùn)行在應(yīng)用程序域(AppDomain)中,這也許讓你想到Win32進(jìn)程,實(shí)際上它們還是有很大的不同。應(yīng)用程序域提供了一種安全而通用的處理單元,公共語言運(yùn)行庫可使用它來隔離應(yīng)用程序。注意在.Net中應(yīng)用程序的隔離是應(yīng)用程序域而不是進(jìn)程,在單個(gè)進(jìn)程中可以存在幾個(gè)應(yīng)用程序域,而且線程可以跨越應(yīng)用程序域的范圍,某個(gè)線程中的方法可以調(diào)用另一個(gè)線程的方法,這樣的話就不會造成進(jìn)程間調(diào)用或進(jìn)程間切換等方面的額外開銷??梢哉f應(yīng)用程序域是物理進(jìn)程(也即win32中的Process)內(nèi)的邏輯進(jìn)程。在

2、Visul C#中System.Threading 命名空間提供一些使得可以進(jìn)行多線程編程的類和接口,其中線程的創(chuàng)建有以下三種方法:Thread、ThreadPool、Timer。下面我就它們的使用方法逐個(gè)作一簡單介紹。1 Thread這也許是最復(fù)雜的方法,但它提供了對線程的各種靈活控制。首先你必須使用它的構(gòu)造函數(shù)創(chuàng)建一個(gè)線程實(shí)例,它的參數(shù)比較簡單,只有一個(gè)ThreadStart 委托:C#public Thread(ThreadStart start);然后調(diào)用Start()啟動它,當(dāng)然你可以利用它的Priority屬性來設(shè)置或獲得它的運(yùn)行優(yōu)先級(enum ThreadPriori

3、ty:  Normal、 Lowest、 Highest、 BelowNormal、 AboveNormal)。見下例:它首先生成了兩個(gè)線程實(shí)例t1和t2,然后分別設(shè)置它們的優(yōu)先級,接著啟動兩線程(兩線程基本一樣,只不過它們輸出不一樣,t1為“1”,t2為“2”,根據(jù)它們各自輸出字符個(gè)數(shù)比可大致看出它們占用CPU時(shí)間之比,這也反映出了它們各自的優(yōu)先級)。 static void Main(string args)     Thread t1 = new Thread(new ThreadStart(Thread1);

4、0;  Thread t2 = new Thread(new ThreadStart(Thread2);   t1.Priority = ThreadPriority.BelowNormal  ;   t2.Priority = ThreadPriority.Lowest ;    t1.Start(); t2.Start();     public static void Thread1()   

5、0;  for (int i = 1; i < 1000; i+)    /每運(yùn)行一個(gè)循環(huán)就寫一個(gè)“1”dosth();    Console.Write("1");        public static void Thread2()      for (int i = 0; i < 1000; i+)    /

6、每運(yùn)行一個(gè)循環(huán)就寫一個(gè)“2”    dosth();    Console.Write("2");       public static void dosth()  /用來模擬復(fù)雜運(yùn)算   for (int j = 0; j < 10000000; j+)           &#

7、160;int a=15;    a = a*a*a*a;     以上程序運(yùn)行結(jié)果為:推薦精選111111111111111111111111111111111111111111211111111111111111111111111111111111111111121111111111111111111111111111111111111111112111111111111111111111111111111111111111111211111111111111111111111111111111111

8、111111121111111111111111111111111111111111111111112  從以上結(jié)果我們可以看出,t1線程所占用CPU的時(shí)間遠(yuǎn)比t2的多,這是因?yàn)閠1的優(yōu)先級比t2的高,若我們把t1和t2的優(yōu)先級都設(shè)為Normal,那結(jié)果是如何?它們所占用的CPU時(shí)間會一樣嗎?是的,正如你所料,見下圖: 12121122121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212

9、1212推薦精選121212121212121212   從上例我們可看出,它的構(gòu)造類似于win32的工作線程,但更加簡單,只需把線程要調(diào)用的函數(shù)作為委托,然后把委托作為參數(shù)構(gòu)造線程實(shí)例即可。當(dāng)調(diào)用Start()啟動后,便會調(diào)用相應(yīng)的函數(shù),從那函數(shù)第一行開始執(zhí)行。 接下來我們結(jié)合線程的ThreadState屬性來了解線程的控制。ThreadState是一個(gè)枚舉類型,它反映的是線程所處的狀態(tài)。當(dāng)一個(gè)Thread實(shí)例剛創(chuàng)建時(shí),它的ThreadState是Unstarted;當(dāng)此線程被調(diào)用Start()啟動之后,它的ThreadState是 Runn

10、ing; 在此線程啟動之后,如果想讓它暫停(阻塞),可以調(diào)用Thread.Sleep() 方法,它有兩個(gè)重載方法(Sleep(int )、Sleep(Timespan )),只不過是表示時(shí)間量的格式不同而已,當(dāng)在某線程內(nèi)調(diào)用此函數(shù)時(shí),它表示此線程將阻塞一段時(shí)間(時(shí)間是由傳遞給 Sleep 的毫秒數(shù)或Timespan決定的,但若參數(shù)為0則表示掛起此線程以使其它線程能夠執(zhí)行,指定 Infinite 以無限期阻塞線程),此時(shí)它的ThreadState將變?yōu)閃aitSleepJoin,另外值得注意一點(diǎn)的是Sleep()函數(shù)被定義為了static?! 這也意味著它不能和某個(gè)線程實(shí)例結(jié)合起來用,也即不存在

11、類似于t1.Sleep(10)的調(diào)用!正是如此,Sleep()函數(shù)只能由需“Sleep”的線程自己調(diào)用,不允許其它線程調(diào)用,正如when to Sleep是個(gè)人私事不能由它人決定。但是當(dāng)某線程處于WaitSleepJoin狀態(tài)而又不得不喚醒它時(shí),可使用Thread.Interrupt 方法 ,它將在線程上引發(fā)ThreadInterruptedException,下面我們先看一個(gè)例子(注意Sleep的調(diào)用方法):static void Main(string args)      Thread t1 = new Thread(new Thread

12、Start(Thread1);    t1.Start(); t1.Interrupt ();E.WaitOne ();t1.Interrupt ();   t1.Join();   Console.WriteLine(“t1 is end”);    static AutoResetEvent E = new AutoResetEvent(false);  public static void Thread1()   

13、60;    try   /從參數(shù)可看出將導(dǎo)致休眠    Thread.Sleep(Timeout.Infinite);       catch(System.Threading.ThreadInterruptedException e)   /中斷處理程序    Console.WriteLine (" 1st interrupt"); 

14、;  E.Set ();   try   / 休眠    Thread.Sleep(Timeout.Infinite );       catch(System.Threading.ThreadInterruptedException e)   Console.WriteLine (" 2nd interrupt");   /暫停10秒&

15、#160;   Thread.Sleep (10000);  運(yùn)行結(jié)果為: 1st interrupt  2nd interrupt  (10s后)t1 is end 從上例我們可以看出Thread.Interrupt方法可以把程序從某個(gè)阻塞(WaitSleepJoin)狀態(tài)喚醒進(jìn)入對應(yīng)的中斷處理程序,然后繼續(xù)往下執(zhí)行(它的ThreadState也變?yōu)镽unning),此函數(shù)的使用必須注意以下幾點(diǎn):1 .此方法不僅可喚醒由Sleep導(dǎo)致的阻塞,而且對一切可導(dǎo)致線程進(jìn)入WaitSleepJoin狀態(tài)的方法(如Wait和Join)都有效。

16、如上例所示, 使用時(shí)要把導(dǎo)致線程阻塞的方法放入try塊內(nèi), 并把相應(yīng)的中斷處理程序放入catch塊內(nèi)。2 .對某一線程調(diào)用Interrupt, 如它正處于WaitSleepJoin狀態(tài), 則進(jìn)入相應(yīng)的中斷處理程序執(zhí)行, 若此時(shí)它不處于WaitSleepJoin狀態(tài), 則它后來進(jìn)入此狀態(tài)時(shí), 將被立即中斷。若在中斷前調(diào)用幾次Interrupt, 只有第一次調(diào)用有效, 這正是上例我用同步的原因, 這樣才能確保第二次調(diào)用Interrupt在第一個(gè)中斷后調(diào)用,否則的話可能導(dǎo)致第二次調(diào)用無效(若

17、它在第一個(gè)中斷前調(diào)用)。你可以把同步去掉試試,其結(jié)果很可能是:   1st interrupt上例還用了另外兩個(gè)使線程進(jìn)入WaitSleepJoin狀態(tài)的方法:利用同步對象和Thread.Join方法。Join方法的使用比較簡單,它表示在調(diào)用此方法的當(dāng)前線程阻塞直至另一線程(此例中是t1)終止或者經(jīng)過了指定的時(shí)間為止(若它還帶了時(shí)間量參數(shù)),當(dāng)兩個(gè)條件(若有)任一出現(xiàn),它立即結(jié)束WaitSleepJoin狀態(tài)進(jìn)入Running狀態(tài)(可根據(jù).Join方法的返回值判斷為何種條件,為true,則是線程終止;false則是時(shí)間到)。線程的暫停還可用Thread.Suspend方法,

18、當(dāng)某線程處于Running狀態(tài)時(shí)對它調(diào)用Suspend方法,它將進(jìn)入SuspendRequested狀態(tài),但它并不會被立即掛起,直到線程到達(dá)安全點(diǎn)之后它才可以將該線程掛起,此時(shí)它將進(jìn)入Suspended狀態(tài)。如對一個(gè)已處于Suspended的線程調(diào)用則無效,要恢復(fù)運(yùn)行只需調(diào)用Thread.Resume即可。  最后我們談的是線程的銷毀,我們可以對需銷毀的線程調(diào)用Abort方法,它會在此線程上引發(fā)ThreadAbortException。我們可把線程內(nèi)的一些代碼放入try塊內(nèi),并把相應(yīng)處理代碼放入相應(yīng)的catch塊內(nèi),當(dāng)線程正執(zhí)行try塊內(nèi)代碼時(shí)如被調(diào)用Abort,它便會跳入相應(yīng)的ca

19、tch塊內(nèi)執(zhí)行,執(zhí)行完catch快內(nèi)的代碼后它將終止(若catch塊內(nèi)執(zhí)行了ResetAbort則不同了:它將取消當(dāng)前Abort請求,繼續(xù)向下執(zhí)行。所以如要確保某線程終止的最好用Join,如上例)。2 ThreadPool線程池(ThreadPool)是一種相對較簡單的方法,它適應(yīng)于一些需要多個(gè)線程而又較短任務(wù)(如一些常處于阻塞狀態(tài)的線程) ,它的缺點(diǎn)是對創(chuàng)建的線程不能加以控制,也不能設(shè)置其優(yōu)先級。由于每個(gè)進(jìn)程只有一個(gè)線程池,當(dāng)然每個(gè)應(yīng)用程序域也只有一個(gè)線程池(對線),所以你將發(fā)現(xiàn)ThreadPool類的成員函數(shù)都為static! 當(dāng)你首次調(diào)用ThreadPool.QueueUser

20、WorkItem、ThreadPool.RegisterWaitForSingleObject等,便會創(chuàng)建線程池實(shí)例。下面我就線程池當(dāng)中的兩函數(shù)作一介紹:C#public static bool QueueUserWorkItem( 推薦精選/調(diào)用成功則返回trueWaitCallback callBack,/要?jiǎng)?chuàng)建的線程調(diào)用的委托 object state  /傳遞給委托的參數(shù))/它的另一個(gè)重載函數(shù)類似,只是委托不帶參數(shù)而已 此函數(shù)的作用是把要?jiǎng)?chuàng)建的線程排隊(duì)到線程池,當(dāng)線程池的可用線程數(shù)不為零時(shí)(線程池有創(chuàng)建線程數(shù)的限制,缺身值為25),便創(chuàng)建此線程,否則就排隊(duì)到線程池等到它有可用

21、的線程時(shí)才創(chuàng)建。C#public static RegisteredWaitHandle RegisterWaitForSingleObject(   WaitHandle waitObject,/ 要注冊的 WaitHandle   WaitOrTimerCallback callBack,/ 線程調(diào)用的委托   object state,/傳遞給委托的參數(shù)   int TimeOut,/超時(shí),單位為毫秒,   bool executeOnlyOnce file:/是否只執(zhí)行一次); publ

22、ic delegate void WaitOrTimerCallback(   object state,/也即傳遞給委托的參數(shù)   bool timedOut/true表示由于超時(shí)調(diào)用,反之則因?yàn)閣aitObject); 此函數(shù)的作用是創(chuàng)建一個(gè)等待線程,一旦調(diào)用此函數(shù)便創(chuàng)建此線程,在參數(shù)waitObject變?yōu)榻K止?fàn)顟B(tài)或所設(shè)定的時(shí)間TimeOut到了之前,它都處于“阻塞”狀態(tài),值得注意的一點(diǎn)是此“阻塞”與Thread的WaitSleepJoin狀態(tài)有很大的不同:當(dāng)某Thread處于WaitSleepJoin狀態(tài)時(shí)CPU會定期的喚醒它以輪詢更新狀態(tài)信息,

23、然后再次進(jìn)入WaitSleepJoin狀態(tài),線程的切換可是很費(fèi)資源的;而用此函數(shù)創(chuàng)建的線程則不同,在觸發(fā)它運(yùn)行之前,CPU不會切換到此線程,它既不占用CPU的時(shí)間又不浪費(fèi)線程切換時(shí)間,但CPU又如何知道何時(shí)運(yùn)行它?實(shí)際上線程池會生成一些輔助線程用來監(jiān)視這些觸發(fā)條件,一旦達(dá)到條件便啟動相應(yīng)的線程,當(dāng)然這些輔助線程本身也占用時(shí)間,但是如果你需創(chuàng)建較多的等待線程時(shí),使用線程池的優(yōu)勢就越加明顯。見下例:static AutoResetEvent ev=new AutoResetEvent(false); public static int Main(string args) 

24、60; ThreadPool.RegisterWaitForSingleObject( ev, new WaitOrTimerCallback(WaitThreadFunc), 4, 2000, false/表示每次完成等待操作后都重置計(jì)時(shí)器,直到注銷等待 );  ThreadPool.QueueUserWorkItem (new WaitCallback (ThreadFunc),8);  Thread.Sleep (10000);   return 0;

25、0;    public static void ThreadFunc(object b)  Console.WriteLine ("the object is 0",b);  for(int i=0;i<2;i+)   Thread.Sleep (1000);   ev.Set();    public static void WaitThreadFunc(object b,bool t) 

26、; Console.WriteLine ("the object is 0,t is 1",b,t);    其運(yùn)行結(jié)果為: the object is 8the object is 4,t is Falsethe object is 4,t is Falsethe object is 4,t is Truethe object is 4,t is Truethe object is 4,t is True從以上結(jié)果我們可以看出線程ThreadFunc運(yùn)行了1次,而WaitThread

27、Func運(yùn)行了5次。我們可以從WaitOrTimerCallback中的bool t參數(shù)判斷啟動此線程的原因:t為false,則表示由于waitObject,否則則是由于超時(shí)。另外我們也可以通過object b向線程傳遞一些參數(shù)。3 Timer    它適用于需周期性調(diào)用的方法,它不在創(chuàng)建計(jì)時(shí)器的線程中運(yùn)行,它在由系統(tǒng)自動分配的單獨(dú)線程中運(yùn)行。這和Win32中的SetTimer方法類似。它的構(gòu)造為:C#public Timer(   TimerCallback callback,/所需調(diào)用的方法   object

28、state,/傳遞給callback的參數(shù)   int dueTime,/多久后開始調(diào)用callback   int period/調(diào)用此方法的時(shí)間間隔);/ 如果 dueTime 為0,則 callback 立即執(zhí)行它的首次調(diào)用。如果 dueTime 為 Infinite,則 callback 不調(diào)用它的方法。計(jì)時(shí)器被禁用,但使用 Change 方法可以重新啟用它。如果 period 為0或 Infinite,并且 dueTime 不為 Infinite,則 callback 調(diào)用它的方法一次。計(jì)時(shí)器的定期行為被禁用,但使用 Change 方法可以重新啟用它。如果 period 為零 (0) 或 Infinite,并且 dueTime 不為 Infinite,則 callback 調(diào)用它的方法一次。計(jì)時(shí)器的定期行為被禁用,但使用 Change 方法可以重新啟用它。在創(chuàng)建計(jì)時(shí)器之后若想改變它的period和dueTime,我們可以通過調(diào)用Timer的Change方法來改變:C#public bool Change(   int dueTime, 

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(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

提交評論