线程处理教程

线程处理的优点是可以创建使用多个执行线程的应用程序。例如,某一进程可以具有管理与用户交互的用户界面线程,以及在用户界面线程等待用户输入时执行其他任务的辅助线程。
该教程说明各种线程活动: 
    创建和执行线程 
    线程同步 
    线程间交互 
    使用线程池 
    使用 mutex 对象保护共享资源
示例 1:创建线程、启动线程和线程间交互
本示例说明如何创建和启动线程,并显示了同时在同一进程内运行的两个线程间的交互。请注意,不必停止或释放线程。这由 .NET Framework 公共语言运行库自动完成。
程序从创建 Alpha 类型的对象 (oAlpha) 和引用 Alpha 类的 Beta 方法的线程 (oThread) 开始。然后启动该线程。线程的 IsAlive 属性允许程序等待,直到线程被初始化(被创建、被分配等)为止。主线程通过 Thread 访问,而 Sleep 方法通知线程放弃其时间片并在一定毫秒数期间停止执行。然后 oThread 被停止和联接。联接一个线程将使主线程等待它死亡或等待它在指定的时间后过期(有关更多详细信息,请参见 Thread.Join 方法)。最后,程序尝试重新启动 oThread,但由于线程无法在停止(中止)后重新启动而告失败。有关临时停止执行的信息,请参见挂起线程执行。

 1// StopJoin.cs
 2using System;
 3using System.Threading;
 4
 5public class Alpha
 6{
 7
 8   // This method that will be called when the thread is started
 9   public void Beta()
10   {
11      while (true)
12      {
13         Console.WriteLine("Alpha.Beta is running in its own thread.");
14      }

15   }

16}
;
17
18public class Simple
19{
20   public static int Main()
21   {
22      Console.WriteLine("Thread Start/Stop/Join Sample");
23      
24      Alpha oAlpha = new Alpha();
25
26      // Create the thread object, passing in the Alpha.Beta method
27      // via a ThreadStart delegate. This does not start the thread.
28      Thread oThread = new Thread(new ThreadStart(oAlpha.Beta));
29
30      // Start the thread
31      oThread.Start();
32
33      // Spin for a while waiting for the started thread to become
34      // alive:
35      while (!oThread.IsAlive);
36      
37      // Put the Main thread to sleep for 1 millisecond to allow oThread
38      // to do some work:
39      Thread.Sleep(1);
40      
41      // Request that oThread be stopped
42      oThread.Abort();
43      
44      // Wait until oThread finishes. Join also has overloads
45      // that take a millisecond interval or a TimeSpan object.
46      oThread.Join();
47      
48      Console.WriteLine();
49      Console.WriteLine("Alpha.Beta has finished");
50      
51      try 
52      {
53         Console.WriteLine("Try to restart the Alpha.Beta thread");
54         oThread.Start();
55      }

56      catch (ThreadStateException) 
57      {
58         Console.Write("ThreadStateException trying to restart Alpha.Beta. ");
59         Console.WriteLine("Expected since aborted threads cannot be restarted.");
60      }

61      return 0;
62   }

63}

64

输出示例
Thread Start/Stop/Join Sample
Alpha.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 finished
Try to restart the Alpha.Beta thread
ThreadStateException 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 对象的“脉冲”即可完成同步。也就是说,首先产生一个项(此时使用者等待脉冲),然后发生一个脉冲,接着使用者使用所产生的项(此时制造者等待脉冲),依此类推。

  1// MonitorSample.cs
  2// This example shows use of the following methods of the C# lock keyword
  3// and the Monitor class 
  4// in threads:
  5//      Monitor.Pulse(Object)
  6//      Monitor.Wait(Object)
  7using System;
  8using System.Threading;
  9
 10public class MonitorSample
 11{
 12   public static void Main(String[] args)
 13   {
 14      int result = 0;   // Result initialized to say there is no error
 15      Cell cell = new Cell( );
 16
 17      CellProd prod = new CellProd(cell, 20);  // Use cell for storage, 
 18                                               // produce 20 items
 19      CellCons cons = new CellCons(cell, 20);  // Use cell for storage, 
 20                                               // consume 20 items
 21
 22      Thread producer = new Thread(new ThreadStart(prod.ThreadRun));
 23      Thread consumer = new Thread(new ThreadStart(cons.ThreadRun));
 24      // Threads producer and consumer have been created, 
 25      // but not started at this point.
 26
 27      try
 28      {
 29         producer.Start( );
 30         consumer.Start( );
 31
 32         producer.Join( );   // Join both threads with no timeout
 33                             // Run both until done.
 34         consumer.Join( );  
 35      // threads producer and consumer have finished at this point.
 36      }

 37      catch (ThreadStateException e)
 38      {
 39         Console.WriteLine(e);  // Display text of exception
 40         result = 1;            // Result says there was an error
 41      }

 42      catch (ThreadInterruptedException e)
 43      {
 44         Console.WriteLine(e);  // This exception means that the thread
 45                                // was interrupted during a Wait
 46         result = 1;            // Result says there was an error
 47      }

 48      // Even though Main returns void, this provides a return code to 
 49      // the parent process.
 50      Environment.ExitCode = result;
 51   }

 52}

 53
 54public class CellProd
 55{
 56   Cell cell;         // Field to hold cell object to be used
 57   int quantity = 1;  // Field for how many items to produce in cell
 58
 59   public CellProd(Cell box, int request)
 60   {
 61      cell = box;          // Pass in what cell object to be used
 62      quantity = request;  // Pass in how many items to produce in cell
 63   }

 64   public void ThreadRun( )
 65   {
 66      for(int looper=1; looper<=quantity; looper++)
 67         cell.WriteToCell(looper);  // "producing"
 68   }

 69}

 70
 71public class CellCons
 72{
 73   Cell cell;         // Field to hold cell object to be used
 74   int quantity = 1;  // Field for how many items to consume from cell
 75
 76   public CellCons(Cell box, int request)
 77   {
 78      cell = box;          // Pass in what cell object to be used
 79      quantity = request;  // Pass in how many items to consume from cell
 80   }

 81   public void ThreadRun( )
 82   {
 83      int valReturned;
 84      for(int looper=1; looper<=quantity; looper++)
 85      // Consume the result by placing it in valReturned.
 86         valReturned=cell.ReadFromCell( );
 87   }

 88}

 89
 90public class Cell
 91{
 92   int cellContents;         // Cell contents
 93   bool readerFlag = false;  // State flag
 94   public int ReadFromCell( )
 95   {
 96      lock(this)   // Enter synchronization block
 97      {
 98         if (!readerFlag)
 99         {            // Wait until Cell.WriteToCell is done producing
100            try
101            {
102               // Waits for the Monitor.Pulse in WriteToCell
103               Monitor.Wait(this);
104            }

105            catch (SynchronizationLockException e)
106            {
107               Console.WriteLine(e);
108            }

109            catch (ThreadInterruptedException e)
110            {
111               Console.WriteLine(e);
112            }

113         }

114         Console.WriteLine("Consume: {0}",cellContents);
115         readerFlag = false;    // Reset the state flag to say consuming
116                                // is done.
117         Monitor.Pulse(this);   // Pulse tells Cell.WriteToCell that
118                                // Cell.ReadFromCell is done.
119      }
   // Exit synchronization block
120      return cellContents;
121   }

122   
123   public void WriteToCell(int n)
124   {
125      lock(this)  // Enter synchronization block
126      {
127         if (readerFlag)
128         {      // Wait until Cell.ReadFromCell is done consuming.
129            try
130            {
131               Monitor.Wait(this);   // Wait for the Monitor.Pulse in
132                                     // ReadFromCell
133            }

134            catch (SynchronizationLockException e)
135            {
136               Console.WriteLine(e);
137            }

138            catch (ThreadInterruptedException e)
139            {
140               Console.WriteLine(e);
141            }

142         }

143         cellContents = n;
144         Console.WriteLine("Produce: {0}",cellContents);
145         readerFlag = true;    // Reset the state flag to say producing
146                               // is done
147         Monitor.Pulse(this);  // Pulse tells Cell.ReadFromCell that 
148                               // Cell.WriteToCell is done.
149      }
   // Exit synchronization block
150   }

151}

152

输出示例
Produce: 1
Consume: 1
Produce: 2
Consume: 2
Produce: 3
Consume: 3
...
...
Produce: 20
Consume: 20

示例 3:使用线程池
以下示例显示如何使用线程池。首先创建 ManualResetEvent 对象,此对象使程序能够知道线程池何时运行完所有的工作项。接着,尝试向线程池添加一个线程。如果添加成功,则添加其余的线程(本例中为 4 个)。然后线程池将工作项放入可用线程中。调用 eventX 上的 WaitOne 方法,这会使程序的其余部分等待,直到用 eventX.Set 方法触发事件为止。最后,程序打印出线程上的负载(实际执行某一特定工作项的线程)。

  1// SimplePool.cs
  2// Simple thread pool example
  3using System;
  4using System.Collections;
  5using System.Threading;
  6
  7// Useful way to store info that can be passed as a state on a work item
  8public class SomeState
  9{
 10   public int Cookie;
 11   public SomeState(int iCookie)
 12   {
 13      Cookie = iCookie;
 14   }

 15}

 16
 17public class Alpha
 18{
 19   public Hashtable HashCount;
 20   public ManualResetEvent eventX;
 21   public static int iCount = 0;
 22   public static int iMaxCount = 0;
 23   public Alpha(int MaxCount) 
 24   {
 25      HashCount = new Hashtable(MaxCount);
 26      iMaxCount = MaxCount;
 27   }

 28
 29   // Beta is the method that will be called when the work item is
 30   // serviced on the thread pool.
 31   // That means this method will be called when the thread pool has
 32   // an available thread for the work item.
 33   public void Beta(Object state)
 34   {
 35      // Write out the hashcode and cookie for the current thread
 36      Console.WriteLine(" {0} {1} :", Thread.CurrentThread.GetHashCode(),
 37         ((SomeState)state).Cookie);
 38      // The lock keyword allows thread-safe modification
 39      // of variables accessible across multiple threads.
 40      Console.WriteLine(
 41         "HashCount.Count=={0}, Thread.CurrentThread.GetHashCode()=={1}",
 42         HashCount.Count, 
 43         Thread.CurrentThread.GetHashCode());
 44      lock (HashCount) 
 45      {
 46         if (!HashCount.ContainsKey(Thread.CurrentThread.GetHashCode()))
 47            HashCount.Add (Thread.CurrentThread.GetHashCode(), 0);
 48         HashCount[Thread.CurrentThread.GetHashCode()] = 
 49            ((int)HashCount[Thread.CurrentThread.GetHashCode()])+1;
 50      }

 51
 52      // Do some busy work.
 53      // Note: Depending on the speed of your machine, if you 
 54      // increase this number, the dispersement of the thread
 55      // loads should be wider.
 56      int iX  = 2000;
 57      Thread.Sleep(iX);
 58      // The Interlocked.Increment method allows thread-safe modification
 59      // of variables accessible across multiple threads.
 60      Interlocked.Increment(ref iCount);
 61      if (iCount == iMaxCount)
 62      {
 63         Console.WriteLine();
 64         Console.WriteLine("Setting eventX ");
 65         eventX.Set();
 66      }

 67   }

 68}

 69
 70public class SimplePool
 71{
 72   public static int Main(string[] args)
 73   {
 74      Console.WriteLine("Thread Pool Sample:");
 75      bool W2K = false;
 76      int MaxCount = 10;  // Allow a total of 10 threads in the pool
 77      // Mark the event as unsignaled.
 78      ManualResetEvent eventX = new ManualResetEvent(false);
 79      Console.WriteLine("Queuing {0} items to Thread Pool", MaxCount);
 80      Alpha oAlpha = new Alpha(MaxCount);  // Create the work items.
 81      // Make sure the work items have a reference to the signaling event.
 82      oAlpha.eventX = eventX;
 83      Console.WriteLine("Queue to Thread Pool 0");
 84      try
 85      {
 86         // Queue the work items, which has the added effect of checking
 87         // which OS is running.
 88         ThreadPool.QueueUserWorkItem(new WaitCallback(oAlpha.Beta),
 89            new SomeState(0));
 90         W2K = true;
 91      }

 92      catch (NotSupportedException)
 93      {
 94         Console.WriteLine("These API's may fail when called on a non-Windows 2000 system.");
 95         W2K = false;
 96      }

 97      if (W2K)  // If running on an OS which supports the ThreadPool methods.
 98      {
 99         for (int iItem=1;iItem < MaxCount;iItem++)
100         {
101            // Queue the work items:
102            Console.WriteLine("Queue to Thread Pool {0}", iItem);
103            ThreadPool.QueueUserWorkItem(new WaitCallback(oAlpha.Beta),new SomeState(iItem));
104         }

105         Console.WriteLine("Waiting for Thread Pool to drain");
106         // The call to exventX.WaitOne sets the event to wait until
107         // eventX.Set() occurs.
108         // (See oAlpha.Beta).
109         // Wait until event is fired, meaning eventX.Set() was called:
110         eventX.WaitOne(Timeout.Infinite,true);
111         // The WaitOne won't return until the event has been signaled.
112         Console.WriteLine("Thread Pool has been drained (Event fired)");
113         Console.WriteLine();
114         Console.WriteLine("Load across threads");
115         foreach(object o in oAlpha.HashCount.Keys)
116            Console.WriteLine("{0} {1}", o, oAlpha.HashCount[o]);
117      }

118      return 0;
119   }

120}

121

输出示例
注意   下列输出随计算机的不同而不同。
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

示例 4:使用 Mutex 对象
可以使用 mutex 对象保护共享资源不被多个线程或进程同时访问。mutex 对象的状态或者设置为终止(当它不属于任何线程时),或者设置为非终止(当它属于某个线程时)。同时只能有一个线程拥有一个 mutex 对象。例如,为了防止两个线程同时写入共享内存,每个线程在执行访问该共享内存的代码之前等待 mutex 对象的所属权。写入共享内存后,线程将释放该 mutex 对象。
此示例阐释如何在处理线程过程中使用 Mutex 类、AutoResetEvent 类和 WaitHandle 类。它还阐释在处理 mutex 对象过程中所用的方法。

 

 1// Mutex.cs
 2// Mutex object example
 3using System;
 4using System.Threading;
 5
 6public class MutexSample
 7{
 8   static Mutex gM1;
 9   static Mutex gM2;
10   const int ITERS = 100;
11   static AutoResetEvent Event1 = new AutoResetEvent(false);
12   static AutoResetEvent Event2 = new AutoResetEvent(false);
13   static AutoResetEvent Event3 = new AutoResetEvent(false);
14   static AutoResetEvent Event4 = new AutoResetEvent(false);
15   
16   public static void Main(String[] args)
17   {
18      Console.WriteLine("Mutex Sample ");
19      // Create Mutex initialOwned, with name of "MyMutex".
20      gM1 = new Mutex(true,"MyMutex");
21      // Create Mutex initialOwned, with no name.
22      gM2 = new Mutex(true);
23      Console.WriteLine(" - Main Owns gM1 and gM2");
24
25      AutoResetEvent[] evs = new AutoResetEvent[4];
26      evs[0= Event1;    // Event for t1
27      evs[1= Event2;    // Event for t2
28      evs[2= Event3;    // Event for t3
29      evs[3= Event4;    // Event for t4
30
31      MutexSample tm = new MutexSample( );
32      Thread t1 = new Thread(new ThreadStart(tm.t1Start));
33      Thread t2 = new Thread(new ThreadStart(tm.t2Start));
34      Thread t3 = new Thread(new ThreadStart(tm.t3Start));
35      Thread t4 = new Thread(new ThreadStart(tm.t4Start));
36      t1.Start( );   // Does Mutex.WaitAll(Mutex[] of gM1 and gM2)
37      t2.Start( );   // Does Mutex.WaitOne(Mutex gM1)
38      t3.Start( );   // Does Mutex.WaitAny(Mutex[] of gM1 and gM2)
39      t4.Start( );   // Does Mutex.WaitOne(Mutex gM2)
40
41      Thread.Sleep(2000);
42      Console.WriteLine(" - Main releases gM1");
43      gM1.ReleaseMutex( );  // t2 and t3 will end and signal
44
45      Thread.Sleep(1000);
46      Console.WriteLine(" - Main releases gM2");
47      gM2.ReleaseMutex( );  // t1 and t4 will end and signal
48
49      // Waiting until all four threads signal that they are done.
50      WaitHandle.WaitAll(evs); 
51      Console.WriteLine(" Mutex Sample");
52   }

53
54   public void t1Start( )
55   {
56      Console.WriteLine("t1Start started,  Mutex.WaitAll(Mutex[])");
57      Mutex[] gMs = new Mutex[2];
58      gMs[0= gM1;  // Create and load an array of Mutex for WaitAll call
59      gMs[1= gM2;
60      Mutex.WaitAll(gMs);  // Waits until both gM1 and gM2 are released
61      Thread.Sleep(2000);
62      Console.WriteLine("t1Start finished, Mutex.WaitAll(Mutex[]) satisfied");
63      Event1.Set( );      // AutoResetEvent.Set() flagging method is done
64   }

65
66   public void t2Start( )
67   {
68      Console.WriteLine("t2Start started,  gM1.WaitOne( )");
69      gM1.WaitOne( );    // Waits until Mutex gM1 is released
70      Console.WriteLine("t2Start finished, gM1.WaitOne( ) satisfied");
71      Event2.Set( );     // AutoResetEvent.Set() flagging method is done
72   }

73
74   public void t3Start( )
75   {
76      Console.WriteLine("t3Start started,  Mutex.WaitAny(Mutex[])");
77      Mutex[] gMs = new Mutex[2];
78      gMs[0= gM1;  // Create and load an array of Mutex for WaitAny call
79      gMs[1= gM2;
80      Mutex.WaitAny(gMs);  // Waits until either Mutex is released
81      Console.WriteLine("t3Start finished, Mutex.WaitAny(Mutex[])");
82      Event3.Set( );       // AutoResetEvent.Set() flagging method is done
83   }

84
85   public void t4Start( )
86   {
87      Console.WriteLine("t4Start started,  gM2.WaitOne( )");
88      gM2.WaitOne( );   // Waits until Mutex gM2 is released
89      Console.WriteLine("t4Start finished, gM2.WaitOne( )");
90      Event4.Set( );    // AutoResetEvent.Set() flagging method is done
91   }

92}

93

示例输出
Mutex Sample ...
 - Main Owns gM1 and gM2
t1Start started,  Mutex.WaitAll(Mutex[])
t2Start started,  gM1.WaitOne( )
t3Start started,  Mutex.WaitAny(Mutex[])
t4Start started,  gM2.WaitOne( )
 - Main releases gM1
t2Start finished, gM1.WaitOne( ) satisfied
t3Start finished, Mutex.WaitAny(Mutex[])
 - Main releases gM2
t1Start finished, Mutex.WaitAll(Mutex[]) satisfied
t4Start finished, gM2.WaitOne( )
... Mutex Sample
注意   此示例的输出可能在每台计算机上以及每次运行时均各不相同。运行此示例的计算机的速度及其操作系统都能影响输出的顺序。在多线程环境中,事件可能并不按预期的顺序发生。

 

posted @ 2006-04-18 10:52  班子  阅读(333)  评论(0)    收藏  举报
我要啦免费统计