国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

CTHREAD C#

2019-11-17 04:05:37
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
 [NET][THREAD]C#的多線程機(jī)制探索【精】

注:本文中出現(xiàn)的代碼均在.net Framework RC3環(huán)境中運(yùn)行通過(guò)

  一.多線程的概念

  Windows是一個(gè)多任務(wù)的系統(tǒng),如果你使用的是windows 2000及其以上版本,你可以通過(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í)候沒(méi)事可干,在多線程程序中,一個(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)在幾乎沒(méi)有單線程的商業(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.cs
using System;
using System.Threading;

namespace ThreadTest
{
  class RunIt
  {
    [STAThread]
    static void Main(string[] args)
    {
      Thread.CurrentThread.Name="System Thread";//給當(dāng)前線程起名為"System Thread"
Console.WriteLine(Thread.CurrentThread.Name+"'Status:"+Thread.CurrentThread.ThreadState);
      Console.ReadLine();
    }
  }
}


  編譯執(zhí)行后你看到了什么?是的,程序?qū)a(chǎn)生如下輸出:

  System Thread's Status:Running

  在這里,我們通過(guò)Thread類的靜態(tài)屬性CurrentThread獲取了當(dāng)前執(zhí)行的線程,對(duì)其Name屬性賦值“System Thread”,最后還輸出了它的當(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)建和控制線程。注意到程序的頭部,我們使用了如下命名空間:

  using System;
  using System.Threading;


  在.net framework class library中,所有與多線程機(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í)行
1

下面我們就動(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)用程序(Console application),下面這些代碼將讓你體味到完全控制一個(gè)線程的無(wú)窮樂(lè)趣!

  //ThreadTest.cs

  using System;
  using System.Threading;

  namespace ThreadTest
  {
  public class Alpha
    {
      public void Beta()
      {
        while (true)
        {
          Console.WriteLine("Alpha.Beta is running in its own thread.");
        }
      }
    };

    public class Simple
    {
      public static int Main()
      {
        Console.WriteLine("Thread Start/Stop/Join Sample");

        Alpha oAlpha = new Alpha();
        file://這里創(chuàng)建一個(gè)線程,使之執(zhí)行Alpha類的Beta()方法
        Thread oThread = new Thread(new ThreadStart(oAlpha.Beta));
        oThread.Start();
        while (!oThread.IsAlive);
          Thread.Sleep(1);
        oThread.Abort();
        oThread.Join();
        Console.WriteLine();
        Console.WriteLine("Alpha.Beta has finished");
        try
        {
          Console.WriteLine("Try to restart the Alpha.Beta thread");
          oThread.Start();
        }
        catch (ThreadStateException)
        {
          Console.Write("ThreadStateException trying to restart Alpha.Beta. ");
          Console.WriteLine("Expected since aborted threads cannot be restarted.");
          Console.ReadLine();
        }
        return 0;
      }
    }
  }


  這段程序包含兩個(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()方法:

  Alpha oAlpha = new Alpha();
  Thread oThread = new Thread(new ThreadStart(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í)行。

  讀者一定注意到了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,它可以把一段代碼定義為互斥段(critical section),互斥段在一個(gè)時(shí)刻內(nèi)只允許一個(gè)線程進(jìn)入執(zhí)行,而其他線程必須等待。在C#中,關(guān)鍵字lock定義如下:

  lock(expression) statement_block



expression代表你希望跟蹤的對(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.cs
  using System;
  using System.Threading;

  internal class Account
  {
  int balance;
  Random r = new Random();
  internal Account(int initial)
  {
    balance = initial;
  }

  internal int Withdraw(int amount)
  {
    if (balance < 0)
    {
    file://如果balance小于0則拋出異常
    throw new Exception("Negative Balance");
    }
    //下面的代碼保證在當(dāng)前線程修改balance的值完成之前
    //不會(huì)有其他線程也執(zhí)行這段代碼來(lái)修改balance的值
    //因此,balance的值是不可能小于0的
    lock (this)
    {
    Console.WriteLine("Current Thread:"+Thread.CurrentThread.Name);
    file://如果沒(méi)有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可能小于0
    if (balance >= amount)
    {
      Thread.Sleep(5);
      balance = balance - amount;
      return amount;
    }
    else
    {
      return 0; // transaction rejected
    }
    }
  }
  internal void DoTransactions()
  {
    for (int i = 0; i < 100; i++)
    Withdraw(r.Next(-50, 100));
  }
  }

  internal class Test
  {
  static internal Thread[] threads = new Thread[10];
  public static void Main()
  {
    Account acc = new Account (0);
    for (int i = 0; i < 10; i++)
    {
    Thread t = new Thread(new ThreadStart(acc.DoTransactions));
    threads[i] = t;
    }
    for (int i = 0; i < 10; i++)
    threads[i].Name=i.ToString();
    for (int i = 0; i < 10; 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ì)象的情形:

  ......
  Queue oQueue=new Queue();
  ......
  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ì)象,只有等待它使用Monitor.Exit()方法釋放鎖。為了保證線程最終都能釋放鎖,你可以把Monitor.Exit()方法寫在try-catch-finally結(jié)構(gòu)中的finally代碼塊里。對(duì)于任何一個(gè)被Monitor鎖定的對(duì)象,內(nèi)存中都保存著與它相關(guān)的一些信息,其一是現(xiàn)在持有鎖的線程的引用,其二是一個(gè)預(yù)備隊(duì)列,隊(duì)列中保存了已經(jīng)準(zhǔn)備好獲取鎖的線程,其三是一個(gè)等待隊(duì)列,隊(duì)列中保存著當(dāng)前正在等待這個(gè)對(duì)象狀態(tài)改變的隊(duì)列的引用。當(dāng)擁有對(duì)象鎖的線程準(zhǔn)備釋放鎖時(shí),它使用Monitor.Pulse()方法通知等待隊(duì)列中的第一個(gè)線程,于是該線程被轉(zhuǎn)移到預(yù)備隊(duì)列中,當(dāng)對(duì)象鎖被釋放時(shí),在預(yù)備隊(duì)列中的線程可以立即獲得對(duì)象鎖。

  下面是一個(gè)展示如何使用lock關(guān)鍵字和Monitor類來(lái)實(shí)現(xiàn)線程的同步和通訊的例子,也是一個(gè)典型的生產(chǎn)者與消費(fèi)者問(wèn)題。這個(gè)例程中,生產(chǎn)者線程和消費(fèi)者線程是交替進(jìn)行的,生產(chǎn)者寫入一個(gè)數(shù),消費(fèi)者立即讀取并且顯示,我將在注釋中介紹該程序的精要所在。用到的系統(tǒng)命名空間如下:

  using System;
  using System.Threading;



首先,我們定義一個(gè)被操作的對(duì)象的類Cell,在這個(gè)類里,有兩個(gè)方法:ReadFromCell()和WriteToCell。消費(fèi)者線程將調(diào)用ReadFromCell()讀取cellContents的內(nèi)容并且顯示出來(lái),生產(chǎn)者進(jìn)程將調(diào)用WriteToCell()方法向cellContents寫入數(shù)據(jù)。

  public class Cell
  {
  int cellContents; // Cell對(duì)象里邊的內(nèi)容
  bool readerFlag = false; // 狀態(tài)標(biāo)志,為true時(shí)可以讀取,為false則正在寫入
  public int ReadFromCell( )
  {
    lock(this) // Lock關(guān)鍵字保證了什么,請(qǐng)大家看前面對(duì)lock的介紹
    {
    if (!readerFlag)//如果現(xiàn)在不可讀取
    {
      try
      {
      file://等待WriteToCell方法中調(diào)用Monitor.Pulse()方法
      Monitor.Wait(this);
      }
      catch (SynchronizationLockException e)
      {
      Console.WriteLine(e);
      }
      catch (ThreadInterruptedException e)
      {
      Console.WriteLine(e);
      }
    }
    Console.WriteLine("Consume: {0}",cellContents);
    readerFlag = false; file://重置readerFlag標(biāo)志,表示消費(fèi)行為已經(jīng)完成
    Monitor.Pulse(this); file://通知WriteToCell()方法(該方法在另外一個(gè)線程中執(zhí)行,等待中)
    }
    return cellContents;
  }

  public void WriteToCell(int n)
  {
    lock(this)
    {
    if (readerFlag)
    {
      try
      {
      Monitor.Wait(this);
      }
      catch (SynchronizationLockException e)
      {
      file://當(dāng)同步方法(指Monitor類除Enter之外的方法)在非同步的代碼區(qū)被調(diào)用
      Console.WriteLine(e);
      }
      catch (ThreadInterruptedException e)
      {
      file://當(dāng)線程在等待狀態(tài)的時(shí)候中止
      Console.WriteLine(e);
      }
    }
    cellContents = n;
    Console.WriteLine("Produce: {0}",cellContents);
    readerFlag = true;
    Monitor.Pulse(this); file://通知另外一個(gè)線程中正在等待的ReadFromCell()方法
    }
  }
  }


  下面定義生產(chǎn)者CellProd和消費(fèi)者類CellCons,它們都只有一個(gè)方法ThreadRun(),以便在Main()函數(shù)中提供給線程的ThreadStart代理對(duì)象,作為線程的入口。

  public class CellProd
  {
  Cell cell; // 被操作的Cell對(duì)象
  int quantity = 1; // 生產(chǎn)者生產(chǎn)次數(shù),初始化為1

  public CellProd(Cell box, int request)
  {
    //構(gòu)造函數(shù)
    cell = box;
    quantity = request;
  }
  public void ThreadRun( )
  {
    for(int looper=1; looper<=quantity; looper++)
    cell.WriteToCell(looper); file://生產(chǎn)者向操作對(duì)象寫入信息
  }
  }

  public class CellCons
  {
  Cell cell;
  int quantity = 1;

  public CellCons(Cell box, int request)
  {
    cell = box;
    quantity = request;
  }
  public void ThreadRun( )
  {
    int valReturned;
    for(int looper=1; looper<=quantity; looper++)
    valReturned=cell.ReadFromCell( );//消費(fèi)者從操作對(duì)象中讀取信息
  }
  }



然后在下面這個(gè)類MonitorSample的Main()函數(shù)中我們要做的就是創(chuàng)建兩個(gè)線程分別作為生產(chǎn)者和消費(fèi)者,使用CellProd.ThreadRun()方法和CellCons.ThreadRun()方法對(duì)同一個(gè)Cell對(duì)象進(jìn)行操作。

  public class MonitorSample
  {
  public static void Main(String[] args)
  {
    int result = 0; file://一個(gè)標(biāo)志位,如果是0表示程序沒(méi)有出錯(cuò),如果是1表明有錯(cuò)誤發(fā)生
    Cell cell = new Cell( );

    //下面使用cell初始化CellProd和CellCons兩個(gè)類,生產(chǎn)和消費(fèi)次數(shù)均為20次
    CellProd prod = new CellProd(cell, 20);
    CellCons cons = new CellCons(cell, 20);

    Thread producer = new Thread(new ThreadStart(prod.ThreadRun));
    Thread consumer = new Thread(new ThreadStart(cons.ThreadRun));
    //生產(chǎn)者線程和消費(fèi)者線程都已經(jīng)被創(chuàng)建,但是沒(méi)有開始執(zhí)行

    try
    {
    producer.Start( );
    consumer.Start( );

    producer.Join( );
    consumer.Join( );
    Console.ReadLine();
    }
    catch (ThreadStateException e)
    {
    file://當(dāng)線程因?yàn)樗帬顟B(tài)的原因而不能執(zhí)行被請(qǐng)求的操作
    Console.WriteLine(e);
    result = 1;
    }
    catch (ThreadInterruptedException e)
    {
    file://當(dāng)線程在等待狀態(tài)的時(shí)候中止
    Console.WriteLine(e);
    result = 1;
    }
    //盡管Main()函數(shù)沒(méi)有返回值,但下面這條語(yǔ)句可以向父進(jìn)程返回執(zhí)行結(jié)果
    Environment.ExitCode = result;
  }
  }



大家可以看到,在上面的例程中,同步是通過(guò)等待Monitor.Pulse()來(lái)完成的。首先生產(chǎn)者生產(chǎn)了一個(gè)值,而同一時(shí)刻消費(fèi)者處于等待狀態(tài),直到收到生產(chǎn)者的“脈沖(Pulse)”通知它生產(chǎn)已經(jīng)完成,此后消費(fèi)者進(jìn)入消費(fèi)狀態(tài),而生產(chǎn)者開始等待消費(fèi)者完成操作后將調(diào)用Monitor.Pulese()發(fā)出的“脈沖”。它的執(zhí)行結(jié)果很簡(jiǎn)單:

  Produce: 1
  Consume: 1
  Produce: 2
  Consume: 2
  Produce: 3
  Consume: 3
  ...
  ...
  Produce: 20
  Consume: 20


  事實(shí)上,這個(gè)簡(jiǎn)單的例子已經(jīng)幫助我們解決了多線程應(yīng)用程序中可能出現(xiàn)的大問(wèn)題,只要領(lǐng)悟了解決線程間沖突的基本方法,很容易把它應(yīng)用到比較復(fù)雜的程序中去。

  四、線程池和定時(shí)器——多線程的自動(dòng)管理
  在多線程的程序中,經(jīng)常會(huì)出現(xiàn)兩種情況。一種情況下,應(yīng)用程序中的線程把大部分的時(shí)間花費(fèi)在等待狀態(tài),等待某個(gè)事件發(fā)生,然后才能給予響應(yīng);而另外一種情況則是線程平常都處于休眠狀態(tài),只是周期性地被喚醒。在.net framework里邊,我們使用ThreadPool來(lái)對(duì)付第一種情況,使用Timer來(lái)對(duì)付第二種情況。

  ThreadPool類提供一個(gè)由系統(tǒng)維護(hù)的線程池——可以看作一個(gè)線程的容器,該容器需要Windows 2000以上版本的系統(tǒng)支持,因?yàn)槠渲心承┓椒ㄕ{(diào)用了只有高版本的Windows才有的API函數(shù)。你可以使用ThreadPool.QueueUserWorkItem()方法將線程安放在線程池里,該方法的原型如下:

  //將一個(gè)線程放進(jìn)線程池,該線程的Start()方法將調(diào)用WaitCallback代理對(duì)象代表的函數(shù)
  public static bool QueueUserWorkItem(WaitCallback);
  //重載的方法如下,參數(shù)object將傳遞給WaitCallback所代表的方法
  public static bool QueueUserWorkItem(WaitCallback, object);


  要注意的是,ThreadPool類也是一個(gè)靜態(tài)類,你不能也不必要生成它的對(duì)象,而且一旦使用該方法在線程池中添加了一個(gè)項(xiàng)目,那么該項(xiàng)目將是沒(méi)有辦法取消的。在這里你無(wú)需自己建立線程,只需把你要做的工作寫成函數(shù),然后作為參數(shù)傳遞給ThreadPool.QueueUserWorkItem()方法就行了,傳遞的方法就是依靠WaitCallback代理對(duì)象,而線程的建立、管理、運(yùn)行等等工作都是由系統(tǒng)自動(dòng)完成的,你無(wú)須考慮那些復(fù)雜的細(xì)節(jié)問(wèn)題,線程池的優(yōu)點(diǎn)也就在這里體現(xiàn)出來(lái)了,就好像你是公司老板——只需要安排工作,而不必親自動(dòng)手。

下面的例程演示了ThreadPool的用法。首先程序創(chuàng)建了一個(gè)ManualResetEvent對(duì)象,該對(duì)象就像一個(gè)信號(hào)燈,可以利用它的信號(hào)來(lái)通知其它線程,本例中當(dāng)線程池中所有線程工作都完成以后,ManualResetEvent的對(duì)象將被設(shè)置為有信號(hào),從而通知主線程繼續(xù)運(yùn)行。它有幾個(gè)重要的方法:Reset(),Set(),WaitOne()。初始化該對(duì)象時(shí),用戶可以指定其默認(rèn)的狀態(tài)(有信號(hào)/無(wú)信號(hào)),在初始化以后,該對(duì)象將保持原來(lái)的狀態(tài)不變直到它的Reset()或者Set()方法被調(diào)用,Reset()方法將其設(shè)置為無(wú)信號(hào)狀態(tài),Set()方法將其設(shè)置為有信號(hào)狀態(tài)。WaitOne()方法使當(dāng)前線程掛起直到ManualResetEvent對(duì)象處于有信號(hào)狀態(tài),此時(shí)該線程將被激活。然后,程序?qū)⑾蚓€程池中添加工作項(xiàng),這些以函數(shù)形式提供的工作項(xiàng)被系統(tǒng)用來(lái)初始化自動(dòng)建立的線程。當(dāng)所有的線程都運(yùn)行完了以后,ManualResetEvent.Set()方法被調(diào)用,因?yàn)檎{(diào)用了ManualResetEvent.WaitOne()方法而處在等待狀態(tài)的主線程將接收到這個(gè)信號(hào),于是它接著往下執(zhí)行,完成后邊的工作。


  using System;
  using System.Collections;
  using System.Threading;

  //這是用來(lái)保存信息的數(shù)據(jù)結(jié)構(gòu),將作為參數(shù)被傳遞
  public class SomeState
  {
  public int Cookie;
  public SomeState(int iCookie)
  {
    Cookie = iCookie;
  }
  }

  public class Alpha
  {
  public Hashtable HashCount;
  public ManualResetEvent eventX;
  public static int iCount = 0;
  public static int iMaxCount = 0;
  public Alpha(int MaxCount)
  {
    HashCount = new Hashtable(MaxCount);
    iMaxCount = MaxCount;
  }

  file://線程池里的線程將調(diào)用Beta()方法
  public void Beta(Object state)
  {
    //輸出當(dāng)前線程的hash編碼值和Cookie的值
    Console.WriteLine(" {0} {1} :", Thread.CurrentThread.GetHashCode(),
    ((SomeState)state).Cookie);
    Console.WriteLine("HashCount.Count=={0}, Thread.CurrentThread.GetHashCode()=={1}", HashCount.Count, Thread.CurrentThread.GetHashCode());
    lock (HashCount)
    {
    file://如果當(dāng)前的Hash表中沒(méi)有當(dāng)前線程的Hash值,則添加之
    if (!HashCount.ContainsKey(Thread.CurrentThread.GetHashCode()))
      HashCount.Add (Thread.CurrentThread.GetHashCode(), 0);
    HashCount[Thread.CurrentThread.GetHashCode()] =
((int)HashCount[Thread.CurrentThread.GetHashCode()])+1;
    }

    int iX = 2000;
    Thread.Sleep(iX);
    //Interlocked.Increment()操作是一個(gè)原子操作,具體請(qǐng)看下面說(shuō)明
    Interlocked.Increment(ref iCount);
    if (iCount == iMaxCount)
    {
    Console.WriteLine();
    Console.WriteLine("Setting eventX ");
    eventX.Set();
    }
  }
  }

  public class SimplePool
  {
  public static int Main(string[] args)
  {
    Console.WriteLine("Thread Pool Sample:");
    bool W2K = false;
    int MaxCount = 10;//允許線程池中運(yùn)行最多10個(gè)線程
    //新建ManualResetEvent對(duì)象并且初始化為無(wú)信號(hào)狀態(tài)
    ManualResetEvent eventX = new ManualResetEvent(false);
    Console.WriteLine("Queuing {0} items to Thread Pool", MaxCount);
    Alpha oAlpha = new Alpha(MaxCount); file://創(chuàng)建工作項(xiàng)
    //注意初始化oAlpha對(duì)象的eventX屬性
    oAlpha.eventX = eventX;
    Console.WriteLine("Queue to Thread Pool 0");
    try
    {
    file://將工作項(xiàng)裝入線程池
    file://這里要用到Windows 2000以上版本才有的API,所以可能出現(xiàn)NotSupportException異常
    ThreadPool.QueueUserWorkItem(new WaitCallback(oAlpha.Beta),
    new SomeState(0));
    W2K = true;
    }
    catch (NotSupportedException)
    {
    Console.WriteLine("These API's may fail when called on a non-Windows 2000 system.");
    W2K = false;
    }
    if (W2K)//如果當(dāng)前系統(tǒng)支持ThreadPool的方法.
    {
    for (int iItem=1;iItem < MaxCount;iItem++)
    {
      //插入隊(duì)列元素
      Console.WriteLine("Queue to Thread Pool {0}", iItem);
      ThreadPool.QueueUserWorkItem(new WaitCallback(oAlpha.Beta),new SomeState(iItem));
    }
    Console.WriteLine("Waiting for Thread Pool to drain");
    file://等待事件的完成,即線程調(diào)用ManualResetEvent.Set()方法
    eventX.WaitOne(Timeout.Infinite,true);
    file://WaitOne()方法使調(diào)用它的線程等待直到eventX.Set()方法被調(diào)用
    Console.WriteLine("Thread Pool has been drained (Event fired)");
    Console.WriteLine();
    Console.WriteLine("Load across threads");
    foreach(object o in oAlpha.HashCount.Keys)
    Console.WriteLine("{0} {1}", o, oAlpha.HashCount[o]);
    }
    Console.ReadLine();
    return 0;

  }
  }


  程序中有些小地方應(yīng)該引起我們的注意。SomeState類是一個(gè)保存信息的數(shù)據(jù)結(jié)構(gòu),在上面的程序中,它作為參數(shù)被傳遞給每一個(gè)線程,你很容易就能理解這個(gè),因?yàn)槟阈枰岩恍┯杏玫男畔⒎庋b起來(lái)提供給線程,而這種方式是非常有效的。程序出現(xiàn)的InterLocked類也是專為多線程程序而存在的,它提供了一些有用的原子操作,所謂原子操作就是在多線程程序中,如果這個(gè)線程調(diào)用這個(gè)操作修改一個(gè)變量,那么其他線程就不能修改這個(gè)變量了,這跟lock關(guān)鍵字在本質(zhì)上是一樣的。

  我們應(yīng)該徹底地分析上面的程序,把握住線程池的本質(zhì),理解它存在的意義是什么,這樣我們才能得心應(yīng)手地使用它。下面是該程序的輸出結(jié)果:

  Thread Pool Sample:
  Queuing 10 items to Thread Pool
  Queue to Thread Pool 0
  Queue to Thread Pool 1
  ...
  ...
  Queue to Thread Pool 9
  Waiting for Thread Pool to drain
  98 0 :
  HashCount.Count==0, Thread.CurrentThread.GetHashCode()==98
  100 1 :
  HashCount.Count==1, Thread.CurrentThread.GetHashCode()==100
  98 2 :
  ...
  ...
  Setting eventX
  Thread Pool has been drained (Event fired)
  Load across threads
  101 2
  100 3
  98 4
  102 1


  與ThreadPool類不同,Timer類的作用是設(shè)置一個(gè)定時(shí)器,定時(shí)執(zhí)行用戶指定的函數(shù),而這個(gè)函數(shù)的傳遞是靠另外一個(gè)代理對(duì)象TimerCallback,它必須在創(chuàng)建Timer對(duì)象時(shí)就指定,并且不能更改。定時(shí)器啟動(dòng)后,系統(tǒng)將自動(dòng)建立一個(gè)新的線程,并且在這個(gè)線程里執(zhí)行用戶指定的函數(shù)。下面的語(yǔ)句初始化了一個(gè)Timer對(duì)象:

  Timer timer = new Timer(timerDelegate, s,1000, 1000);


  第一個(gè)參數(shù)指定了TimerCallback代理對(duì)象;第二個(gè)參數(shù)的意義跟上面提到的WaitCallback代理對(duì)象的一樣,作為一個(gè)傳遞數(shù)據(jù)的對(duì)象傳遞給要調(diào)用的方法;第三個(gè)參數(shù)是延遲時(shí)間——計(jì)時(shí)開始的時(shí)刻距現(xiàn)在的時(shí)間,單位是毫秒;第四個(gè)參數(shù)是定時(shí)器的時(shí)間間隔——計(jì)時(shí)開始以后,每隔這么長(zhǎng)的一段時(shí)間,TimerCallback所代表的方法將被調(diào)用一次,單位也是毫秒。這句話的意思就是將定時(shí)器的延遲時(shí)間和時(shí)間間隔都設(shè)為1秒鐘。

  定時(shí)器的設(shè)置是可以改變的,只要調(diào)用Timer.Change()方法,這是一個(gè)參數(shù)類型重載的方法,一般使用的原型如下:

   public bool Change(long, long);


  下面這段代碼將前邊設(shè)置的定時(shí)器修改了一下:

   timer.Change(10000,2000);


  很顯然,定時(shí)器timer的時(shí)間間隔被重新設(shè)置為2秒,停止計(jì)時(shí)10秒后生效。

  下面這段程序演示了Timer類的用法。


  using System;
  using System.Threading;
  class TimerExampleState
  {
  public int counter = 0;
  public Timer tmr;
  }

  class App
  {
  public static void Main()
  {
    TimerExampleState s = new TimerExampleState();

    //創(chuàng)建代理對(duì)象TimerCallback,該代理將被定時(shí)調(diào)用
    TimerCallback timerDelegate = new TimerCallback(CheckStatus);

    //創(chuàng)建一個(gè)時(shí)間間隔為1s的定時(shí)器
    Timer timer = new Timer(timerDelegate, s,1000, 1000);
    s.tmr = timer;

    //主線程停下來(lái)等待Timer對(duì)象的終止
    while(s.tmr != null)
    Thread.Sleep(0);
    Console.WriteLine("Timer example done.");
    Console.ReadLine();
  }
  file://下面是被定時(shí)調(diào)用的方法

  static void CheckStatus(Object state)
  {
    TimerExampleState s =(TimerExampleState)state;
    s.counter++;
    Console.WriteLine("{0} Checking Status {1}.",DateTime.Now.TimeOfDay, s.counter);
    if(s.counter == 5)
    {
    file://使用Change方法改變了時(shí)間間隔
    (s.tmr).Change(10000,2000);
    Console.WriteLine("changed...");
    }
    if(s.counter == 10)
    {
    Console.WriteLine("disposing of timer...");
    s.tmr.Dispose();
    s.tmr = null;
    }
  }
  }


  程序首先創(chuàng)建了一個(gè)定時(shí)器,它將在創(chuàng)建1秒之后開始每隔1秒調(diào)用一次CheckStatus()方法,當(dāng)調(diào)用5次以后,在CheckStatus()方法中修改了時(shí)間間隔為2秒,并且指定在10秒后重新開始。當(dāng)計(jì)數(shù)達(dá)到10次,調(diào)用Timer.Dispose()方法刪除了timer對(duì)象,主線程于是跳出循環(huán),終止程序。程序執(zhí)行的結(jié)果如下:







  上面就是對(duì)ThreadPool和Timer兩個(gè)類的簡(jiǎn)單介紹,充分利用系統(tǒng)提供的功能,可以為我們省去很多時(shí)間和精力——特別是對(duì)很容易出錯(cuò)的多線程程序。同時(shí)我們也可以看到.net Framework強(qiáng)大的內(nèi)置對(duì)象,這些將對(duì)我們的編程帶來(lái)莫大的方便。

、互斥對(duì)象——更加靈活的同步方式

  有時(shí)候你會(huì)覺得上面介紹的方法好像不夠用,對(duì),我們解決了代碼和資源的同步問(wèn)題,解決了多線程自動(dòng)化管理和定時(shí)觸發(fā)的問(wèn)題,但是如何控制多個(gè)線程相互之間的聯(lián)系呢?例如我要到餐廳吃飯,在吃飯之前我先得等待廚師把飯菜做好,之后我開始吃飯,吃完我還得付款,付款方式可以是現(xiàn)金,也可以是信用卡,付款之后我才能離開。分析一下這個(gè)過(guò)程,我吃飯可以看作是主線程,廚師做飯又是一個(gè)線程,服務(wù)員用信用卡收款和收現(xiàn)金可以看作另外兩個(gè)線程,大家可以很清楚地看到其中的關(guān)系——我吃飯必須等待廚師做飯,然后等待兩個(gè)收款線程之中任意一個(gè)的完成,然后我吃飯這個(gè)線程可以執(zhí)行離開這個(gè)步驟,于是我吃飯才算結(jié)束了。事實(shí)上,現(xiàn)實(shí)中有著比這更復(fù)雜的聯(lián)系,我們?cè)鯓硬拍芎芎玫乜刂扑鼈兌划a(chǎn)生沖突和重復(fù)呢?

  這種情況下,我們需要用到互斥對(duì)象,即System.Threading命名空間中的Mutex類。大家一定坐過(guò)出租車吧,事實(shí)上我們可以把Mutex看作一個(gè)出租車,那么乘客就是線程了,乘客首先得等車,然后上車,最后下車,當(dāng)一個(gè)乘客在車上時(shí),其他乘客就只有等他下車以后才可以上車。而線程與Mutex對(duì)象的關(guān)系也正是如此,線程使用Mutex.WaitOne()方法等待Mutex對(duì)象被釋放,如果它等待的Mutex對(duì)象被釋放了,它就自動(dòng)擁有這個(gè)對(duì)象,直到它調(diào)用Mutex.ReleaseMutex()方法釋放這個(gè)對(duì)象,而在此期間,其他想要獲取這個(gè)Mutex對(duì)象的線程都只有等待。

  下面這個(gè)例子使用了Mutex對(duì)象來(lái)同步四個(gè)線程,主線程等待四個(gè)線程的結(jié)束,而這四個(gè)線程的運(yùn)行又是與兩個(gè)Mutex對(duì)象相關(guān)聯(lián)的。其中還用到AutoResetEvent類的對(duì)象,如同上面提到的ManualResetEvent對(duì)象一樣,大家可以把它簡(jiǎn)單地理解為一個(gè)信號(hào)燈,使用AutoResetEvent.Set()方法可以設(shè)置它為有信號(hào)狀態(tài),而使用AutoResetEvent.Reset()方法把它設(shè)置為無(wú)信號(hào)狀態(tài)。這里用它的有信號(hào)狀態(tài)來(lái)表示一個(gè)線程的結(jié)束。


  // Mutex.cs
  using System;
  using System.Threading;

  public class MutexSample
  {
  static Mutex gM1;
  static Mutex gM2;
  const int ITERS = 100;
  static AutoResetEvent Event1 = new AutoResetEvent(false);
  static AutoResetEvent Event2 = new AutoResetEvent(false);
  static AutoResetEvent Event3 = new AutoResetEvent(false);
  static AutoResetEvent Event4 = new AutoResetEvent(false);

  public static void Main(String[] args)
  {
    Console.WriteLine("Mutex Sample ...");
    //創(chuàng)建一個(gè)Mutex對(duì)象,并且命名為MyMutex
    gM1 = new Mutex(true,"MyMutex");
    //創(chuàng)建一個(gè)未命名的Mutex 對(duì)象.
    gM2 = new Mutex(true);
    Console.WriteLine(" - Main Owns gM1 and gM2");

    AutoResetEvent[] evs = new AutoResetEvent[4];
    evs[0] = Event1; file://為后面的線程t1,t2,t3,t4定義AutoResetEvent對(duì)象
    evs[1] = Event2;
    evs[2] = Event3;
    evs[3] = Event4;

    MutexSample tm = new MutexSample( );
    Thread t1 = new Thread(new ThreadStart(tm.t1Start));
    Thread t2 = new Thread(new ThreadStart(tm.t2Start));
    Thread t3 = new Thread(new ThreadStart(tm.t3Start));
    Thread t4 = new Thread(new ThreadStart(tm.t4Start));
    t1.Start( );// 使用Mutex.WaitAll()方法等待一個(gè)Mutex數(shù)組中的對(duì)象全部被釋放
    t2.Start( );// 使用Mutex.WaitOne()方法等待gM1的釋放
    t3.Start( );// 使用Mutex.WaitAny()方法等待一個(gè)Mutex數(shù)組中任意一個(gè)對(duì)象被釋放
    t4.Start( );// 使用Mutex.WaitOne()方法等待gM2的釋放


    Thread.Sleep(2000);
    Console.WriteLine(" - Main releases gM1");
    gM1.ReleaseMutex( ); file://線程t2,t3結(jié)束條件滿足

    Thread.Sleep(1000);
    Console.WriteLine(" - Main releases gM2");
    gM2.ReleaseMutex( ); file://線程t1,t4結(jié)束條件滿足

    //等待所有四個(gè)線程結(jié)束
    WaitHandle.WaitAll(evs);
    Console.WriteLine("... Mutex Sample");
    Console.ReadLine();
  }

  public void t1Start( )
  {
    Console.WriteLine("t1Start started, Mutex.WaitAll(Mutex[])");
    Mutex[] gMs = new Mutex[2];
    gMs[0] = gM1;//創(chuàng)建一個(gè)Mutex數(shù)組作為Mutex.WaitAll()方法的參數(shù)
    gMs[1] = gM2;
    Mutex.WaitAll(gMs);//等待gM1和gM2都被釋放
    Thread.Sleep(2000);
    Console.WriteLine("t1Start finished, Mutex.WaitAll(Mutex[]) satisfied");
    Event1.Set( ); file://線程結(jié)束,將Event1設(shè)置為有信號(hào)狀態(tài)
  }

  public void t2Start( )
  {
    Console.WriteLine("t2Start started, gM1.WaitOne( )");
    gM1.WaitOne( );//等待gM1的釋放
    Console.WriteLine("t2Start finished, gM1.WaitOne( ) satisfied");
    Event2.Set( );//線程結(jié)束,將Event2設(shè)置為有信號(hào)狀態(tài)
  }

  public void t3Start( )
  {
    Console.WriteLine("t3Start started, Mutex.WaitAny(Mutex[])");
    Mutex[] gMs = new Mutex[2];
    gMs[0] = gM1;//創(chuàng)建一個(gè)Mutex數(shù)組作為Mutex.WaitAny()方法的參數(shù)
    gMs[1] = gM2;
    Mutex.WaitAny(gMs);//等待數(shù)組中任意一個(gè)Mutex對(duì)象被釋放
    Console.WriteLine("t3Start finished, Mutex.WaitAny(Mutex[])");
    Event3.Set( );//線程結(jié)束,將Event3設(shè)置為有信號(hào)狀態(tài)
  }

  public void t4Start( )
  {
    Console.WriteLine("t4Start started, gM2.WaitOne( )");
    gM2.WaitOne( );//等待gM2被釋放
    Console.WriteLine("t4Start finished, gM2.WaitOne( )");
    Event4.Set( );//線程結(jié)束,將Event4設(shè)置為有信號(hào)狀態(tài)
  }
  }


  下面是該程序的執(zhí)行結(jié)果:







  從執(zhí)行結(jié)果可以很清楚地看到,線程t2,t3的運(yùn)行是以gM1的釋放為條件的,而t4在gM2釋放后開始執(zhí)行,t1則在gM1和gM2都被釋放了之后才執(zhí)行。Main()函數(shù)最后,使用WaitHandle等待所有的AutoResetEvent對(duì)象的信號(hào),這些對(duì)象的信號(hào)代表相應(yīng)線程的結(jié)束。

  六、小結(jié)

  多線程程序設(shè)計(jì)是一個(gè)龐大的主題,而本文試圖在.net Framework環(huán)境下,使用最新的C#語(yǔ)言來(lái)描述多線程程序的概貌。希望本文能有助于大家理解線程這種概念,理解多線程的用途,理解它的C#實(shí)現(xiàn)方法,理解線程將為我們帶來(lái)的好處和麻煩。C#是一種新的語(yǔ)言,因此它的線程機(jī)制也有許多獨(dú)特的地方,希望大家能通過(guò)本文清楚地看到這些,從而可以對(duì)線程進(jìn)行更深入的理解和探索。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 西乌| 康保县| 西丰县| 通城县| 台中县| 宁津县| 屏山县| 康马县| 万荣县| 常山县| 福清市| 九寨沟县| 岳普湖县| 呼伦贝尔市| 湖口县| 高要市| 外汇| 五原县| 清镇市| 绥中县| 金川县| 侯马市| 北流市| 濉溪县| 乌鲁木齐市| 岚皋县| 鹤山市| 航空| 云龙县| 濉溪县| 大冶市| 云和县| 岳普湖县| 留坝县| 巴彦淖尔市| 浮山县| 灯塔市| 白山市| 北京市| 安国市| 青海省|