C# 線程處理教程

來源:互聯網
上載者:User

線程處理的優點是可以建立使用多個執行線程的應用程式。例如,某一進程可以具有管理與使用者互動的使用介面執行緒,以及在使用介面執行緒等待使用者輸入時執行其他任務的輔助線程。

該教程說明各種線程活動:

  • 建立和執行線程
  • 線程同步
  • 線程間互動
  • 使用線程池
  • 使用 mutex 對象保護共用資源
教程

該教程包含下列樣本:

  • 樣本 1:建立線程、啟動線程和線程間互動
  • 樣本 2:同步兩個線程:製造者和使用者
  • 樣本 3:使用線程池
  • 樣本 4:使用 Mutex 對象
樣本 1:建立線程、啟動線程和線程間互動

本樣本說明如何建立和啟動線程,並顯示了同時在同一進程內啟動並執行兩個線程間的互動。請注意,不必停止或釋放線程。這由 .NET Framework 公用語言運行庫自動完成。

程式從建立 Alpha 類型的對象 (oAlpha) 和引用 Alpha 類的 Beta 方法的線程 (oThread) 開始。然後啟動該線程。線程的 IsAlive 屬性允許程式等待,直到線程被初始化(被建立、被分配等)為止。主線程通過 Thread 訪問,而 Sleep 方法通知線程放棄其時間片並在一定毫秒數期間停止執行。然後 oThread 被停止和聯結。聯結一個線程將使主線程等待它死亡或等待它在指定的時間後到期。最後,程式嘗試重新啟動 oThread,但由於線程無法在停止(中止)後重新啟動而告失敗。有關臨時停止執行的資訊,請參見掛起線程執行。

// StopJoin.csusing System;using System.Threading;public class Alpha{   // This method that will be called when the thread is started   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();      // Create the thread object, passing in the Alpha.Beta method      // via a ThreadStart delegate. This does not start the thread.      Thread oThread = new Thread(new ThreadStart(oAlpha.Beta));      // Start the thread      oThread.Start();      // Spin for a while waiting for the started thread to become      // alive:      while (!oThread.IsAlive);            // Put the Main thread to sleep for 1 millisecond to allow oThread      // to do some work:      Thread.Sleep(1);            // Request that oThread be stopped      oThread.Abort();            // Wait until oThread finishes. Join also has overloads      // that take a millisecond interval or a TimeSpan object.      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.");      }      return 0;   }}
輸出樣本
Thread Start/Stop/Join SampleAlpha.Beta is running in its own thread.Alpha.Beta is running in its own thread.Alpha.Beta is running in its own thread.......Alpha.Beta has finishedTry to restart the Alpha.Beta threadThreadStateException trying to restart Alpha.Beta. Expected since aborted threads cannot be restarted.
樣本 2:同步兩個線程:製造者和使用者

下面的樣本顯示如何使用 C# lock 關鍵字和 Monitor 對象的 Pulse 方法完成同步。Pulse 方法通知等待隊列中的線程對象的狀態已更改。(有關脈衝的更多詳細資料,請參見 Monitor.Pulse 方法)。

本樣本建立一個 Cell 對象,它具有兩個方法:ReadFromCell 和 WriteToCell。從 CellProd 和 CellCons 類建立另外兩個對象;這兩個對象均具有調用 ReadFromCell 和 WriteToCell 的 ThreadRun 方法。通過等待依次到達的來自 Monitor 對象的“脈衝”即可完成同步。也就是說,首先產生一個項(此時使用者等待脈衝),然後發生一個脈衝,接著使用者使用所產生的項(此時製造者等待脈衝),依此類推。

// MonitorSample.cs// This example shows use of the following methods of the C# lock keyword// and the Monitor class // in threads://      Monitor.Pulse(Object)//      Monitor.Wait(Object)using System;using System.Threading;public class MonitorSample{   public static void Main(String[] args)   {      int result = 0;   // Result initialized to say there is no error      Cell cell = new Cell( );      CellProd prod = new CellProd(cell, 20);  // Use cell for storage,                                                // produce 20 items      CellCons cons = new CellCons(cell, 20);  // Use cell for storage,                                                // consume 20 items      Thread producer = new Thread(new ThreadStart(prod.ThreadRun));      Thread consumer = new Thread(new ThreadStart(cons.ThreadRun));      // Threads producer and consumer have been created,       // but not started at this point.      try      {         producer.Start( );         consumer.Start( );         producer.Join( );   // Join both threads with no timeout                             // Run both until done.         consumer.Join( );        // threads producer and consumer have finished at this point.      }      catch (ThreadStateException e)      {         Console.WriteLine(e);  // Display text of exception         result = 1;            // Result says there was an error      }      catch (ThreadInterruptedException e)      {         Console.WriteLine(e);  // This exception means that the thread                                // was interrupted during a Wait         result = 1;            // Result says there was an error      }      // Even though Main returns void, this provides a return code to       // the parent process.      Environment.ExitCode = result;   }}public class CellProd{   Cell cell;         // Field to hold cell object to be used   int quantity = 1;  // Field for how many items to produce in cell   public CellProd(Cell box, int request)   {      cell = box;          // Pass in what cell object to be used      quantity = request;  // Pass in how many items to produce in cell   }   public void ThreadRun( )   {      for(int looper=1; looper<=quantity; looper++)         cell.WriteToCell(looper);  // "producing"   }}public class CellCons{   Cell cell;         // Field to hold cell object to be used   int quantity = 1;  // Field for how many items to consume from cell   public CellCons(Cell box, int request)   {      cell = box;          // Pass in what cell object to be used      quantity = request;  // Pass in how many items to consume from cell   }   public void ThreadRun( )   {      int valReturned;      for(int looper=1; looper<=quantity; looper++)      // Consume the result by placing it in valReturned.         valReturned=cell.ReadFromCell( );   }}public class Cell{   int cellContents;         // Cell contents   bool readerFlag = false;  // State flag   public int ReadFromCell( )   {      lock(this)   // Enter synchronization block      {         if (!readerFlag)         {            // Wait until Cell.WriteToCell is done producing            try            {               // Waits for the Monitor.Pulse in WriteToCell               Monitor.Wait(this);            }            catch (SynchronizationLockException e)            {               Console.WriteLine(e);            }            catch (ThreadInterruptedException e)            {               Console.WriteLine(e);            }         }         Console.WriteLine("Consume: {0}",cellContents);         readerFlag = false;    // Reset the state flag to say consuming                                // is done.         Monitor.Pulse(this);   // Pulse tells Cell.WriteToCell that                                // Cell.ReadFromCell is done.      }   // Exit synchronization block      return cellContents;   }      public void WriteToCell(int n)   {      lock(this)  // Enter synchronization block      {         if (readerFlag)         {      // Wait until Cell.ReadFromCell is done consuming.            try            {               Monitor.Wait(this);   // Wait for the Monitor.Pulse in                                     // ReadFromCell            }            catch (SynchronizationLockException e)            {               Console.WriteLine(e);            }            catch (ThreadInterruptedException e)            {               Console.WriteLine(e);            }         }         cellContents = n;         Console.WriteLine("Produce: {0}",cellContents);         readerFlag = true;    // Reset the state flag to say producing                               // is done         Monitor.Pulse(this);  // Pulse tells Cell.ReadFromCell that                                // Cell.WriteToCell is done.      }   // Exit synchronization block   }}
輸出樣本
Produce: 1Consume: 1Produce: 2Consume: 2Produce: 3Consume: 3......Produce: 20Consume: 20
樣本 3:使用線程池

以下樣本顯示如何使用線程池。首先建立 ManualResetEvent 對象,此對象使程式能夠知道線程池何時運行完所有的工作項目。接著,嘗試向線程池添加一個線程。如果添加成功,則添加其餘的線程(本例中為 4 個)。然後線程池將工作項目放入可用線程中。調用 eventX 上的 WaitOne 方法,這會使程式的其餘部分等待,直到用 eventX.Set 方法觸發事件為止。最後,程式列印出線程上的負載(實際執行某一特定工作項目的線程)。

// SimplePool.cs// Simple thread pool exampleusing System;using System.Collections;using System.Threading;// Useful way to store info that can be passed as a state on a work itempublic 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;   }   // Beta is the method that will be called when the work item is   // serviced on the thread pool.   // That means this method will be called when the thread pool has   // an available thread for the work item.   public void Beta(Object state)   {      // Write out the hashcode and cookie for the current thread      Console.WriteLine(" {0} {1} :", Thread.CurrentThread.GetHashCode(),         ((SomeState)state).Cookie);      // The lock keyword allows thread-safe modification      // of variables accessible across multiple threads.      Console.WriteLine(         "HashCount.Count=={0}, Thread.CurrentThread.GetHashCode()=={1}",         HashCount.Count,          Thread.CurrentThread.GetHashCode());      lock (HashCount)       {         if (!HashCount.ContainsKey(Thread.CurrentThread.GetHashCode()))            HashCount.Add (Thread.CurrentThread.GetHashCode(), 0);         HashCount[Thread.CurrentThread.GetHashCode()] =             ((int)HashCount[Thread.CurrentThread.GetHashCode()])+1;      }      // Do some busy work.      // Note: Depending on the speed of your machine, if you       // increase this number, the dispersement of the thread      // loads should be wider.      int iX  = 2000;      Thread.Sleep(iX);      // The Interlocked.Increment method allows thread-safe modification      // of variables accessible across multiple threads.      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;  // Allow a total of 10 threads in the pool      // Mark the event as unsignaled.      ManualResetEvent eventX = new ManualResetEvent(false);      Console.WriteLine("Queuing {0} items to Thread Pool", MaxCount);      Alpha oAlpha = new Alpha(MaxCount);  // Create the work items.      // Make sure the work items have a reference to the signaling event.      oAlpha.eventX = eventX;      Console.WriteLine("Queue to Thread Pool 0");      try      {         // Queue the work items, which has the added effect of checking         // which OS is running.         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)  // If running on an OS which supports the ThreadPool methods.      {         for (int iItem=1;iItem < MaxCount;iItem++)         {            // Queue the work items:            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");         // The call to exventX.WaitOne sets the event to wait until         // eventX.Set() occurs.         // (See oAlpha.Beta).         // Wait until event is fired, meaning eventX.Set() was called:         eventX.WaitOne(Timeout.Infinite,true);         // The WaitOne won't return until the event has been signaled.         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]);      }      return 0;   }}
輸出樣本

注意   下列輸出隨電腦的不同而不同。

Thread Pool Sample:Queuing 10 items to Thread PoolQueue to Thread Pool 0Queue to Thread Pool 1......Queue to Thread Pool 9Waiting 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 eventXThread Pool has been drained (Event fired)Load across threads101 2100 398 4102 1
樣本 4:使用 Mutex 對象

可以使用 mutex 對象保護共用資源不被多個線程或進程同時訪問。mutex 對象的狀態或者設定為終止(當它不屬於任何線程時),或者設定為非終止(當它屬於某個線程時)。同時只能有一個線程擁有一個 mutex 對象。例如,為了防止兩個線程同時寫入共用記憶體,每個線程在執行訪問該共用記憶體的代碼之前等待 mutex 對象的所屬權。寫入共用記憶體後,線程將釋放該 mutex 對象。

此樣本闡釋如何在處理線程過程中使用 Mutex 類、AutoResetEvent 類和 WaitHandle 類。它還闡釋在處理 mutex 對象過程中所用的方法。

// Mutex.cs// Mutex object exampleusing 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 ...");      // Create Mutex initialOwned, with name of "MyMutex".      gM1 = new Mutex(true,"MyMutex");      // Create Mutex initialOwned, with no name.      gM2 = new Mutex(true);      Console.WriteLine(" - Main Owns gM1 and gM2");      AutoResetEvent[] evs = new AutoResetEvent[4];      evs[0] = Event1;    // Event for t1      evs[1] = Event2;    // Event for t2      evs[2] = Event3;    // Event for t3      evs[3] = Event4;    // Event for t4      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( );   // Does Mutex.WaitAll(Mutex[] of gM1 and gM2)      t2.Start( );   // Does Mutex.WaitOne(Mutex gM1)      t3.Start( );   // Does Mutex.WaitAny(Mutex[] of gM1 and gM2)      t4.Start( );   // Does Mutex.WaitOne(Mutex gM2)      Thread.Sleep(2000);      Console.WriteLine(" - Main releases gM1");      gM1.ReleaseMutex( );  // t2 and t3 will end and signal      Thread.Sleep(1000);      Console.WriteLine(" - Main releases gM2");      gM2.ReleaseMutex( );  // t1 and t4 will end and signal      // Waiting until all four threads signal that they are done.      WaitHandle.WaitAll(evs);       Console.WriteLine("... Mutex Sample");   }   public void t1Start( )   {      Console.WriteLine("t1Start started,  Mutex.WaitAll(Mutex[])");      Mutex[] gMs = new Mutex[2];      gMs[0] = gM1;  // Create and load an array of Mutex for WaitAll call      gMs[1] = gM2;      Mutex.WaitAll(gMs);  // Waits until both gM1 and gM2 are released      Thread.Sleep(2000);      Console.WriteLine("t1Start finished, Mutex.WaitAll(Mutex[]) satisfied");      Event1.Set( );      // AutoResetEvent.Set() flagging method is done   }   public void t2Start( )   {      Console.WriteLine("t2Start started,  gM1.WaitOne( )");      gM1.WaitOne( );    // Waits until Mutex gM1 is released      Console.WriteLine("t2Start finished, gM1.WaitOne( ) satisfied");      Event2.Set( );     // AutoResetEvent.Set() flagging method is done   }   public void t3Start( )   {      Console.WriteLine("t3Start started,  Mutex.WaitAny(Mutex[])");      Mutex[] gMs = new Mutex[2];      gMs[0] = gM1;  // Create and load an array of Mutex for WaitAny call      gMs[1] = gM2;      Mutex.WaitAny(gMs);  // Waits until either Mutex is released      Console.WriteLine("t3Start finished, Mutex.WaitAny(Mutex[])");      Event3.Set( );       // AutoResetEvent.Set() flagging method is done   }   public void t4Start( )   {      Console.WriteLine("t4Start started,  gM2.WaitOne( )");      gM2.WaitOne( );   // Waits until Mutex gM2 is released      Console.WriteLine("t4Start finished, gM2.WaitOne( )");      Event4.Set( );    // AutoResetEvent.Set() flagging method is done   }}
樣本輸出
Mutex Sample ... - Main Owns gM1 and gM2t1Start started,  Mutex.WaitAll(Mutex[])t2Start started,  gM1.WaitOne( )t3Start started,  Mutex.WaitAny(Mutex[])t4Start started,  gM2.WaitOne( ) - Main releases gM1t2Start finished, gM1.WaitOne( ) satisfiedt3Start finished, Mutex.WaitAny(Mutex[]) - Main releases gM2t1Start finished, Mutex.WaitAll(Mutex[]) satisfiedt4Start finished, gM2.WaitOne( )... Mutex Sample

注意   此樣本的輸出可能在每台電腦上以及每次運行時均各不相同。運行此樣本的電腦的速度及其作業系統都能影響輸出的順序。在多線程環境中,事件可能並不按預期的順序發生。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.