(转载)C#多线程编程之:同步事件和等待句柄
 同步事件和等待句柄用于解决复杂的同步情况。比如一个大的计算步骤包含3个步骤result = first term + second term + third term,如果现在想写个多线程程序,同时计算first term,second term 和third term,等所有3个步骤计算好后再把它们汇总起来,我们就需要使用到同步事件和等待句柄。

  同步事件分有两个,分别为AutoResetEvent和ManualResetEvent,这两个类可以用来代表某个线程的运行状态:终止和非终止。

  等待句柄用来判断ResetEvent的状态,如果是非终止状态就一直等待,否则放行,让等待句柄下面的代码继续运行。

  下面的代码示例阐释了如何使用等待句柄来发送复杂数字计算的不同阶段的完成信号。此计算的格式为:result = first term + second term + third term

 

 

1using System;
2using
System.Threading;
3

4class CalculateTest
5
{
6    static void
Main()
7
    {
8        Calculate calc = new
Calculate();
9

10        Console.WriteLine("Result = {0}.", calc.Result(234).ToString());
11        Console.WriteLine("Result = {0}.", calc.Result(55
).ToString());
12

13        Console.ReadKey();
14
    }
15
}
16

17class Calculate
18
{
19    //基数、数1、数2、数3

20    double baseNumber, firstTerm, secondTerm, thirdTerm;
21    //自动重置同步事件数组

22    AutoResetEvent[] autoEvents;
23    //手动重置同步事件

24    ManualResetEvent manualEvent;
25    //随机数生成器

26    Random randomGenerator;
27

28    public Calculate()
29
    {
30        //初始化同步事件

31        autoEvents = new AutoResetEvent[]
32
                     {
33                         new AutoResetEvent(false
),
34                         new AutoResetEvent(false
),
35                         new AutoResetEvent(false
)
36
                     };
37

38        manualEvent = new ManualResetEvent(false);
39
    }
40

41    void CalculateBase(object stateInfo)
42
    {
43        baseNumber =
randomGenerator.NextDouble();
44

45        //置事件状态为终止状态,使等待线程继续
46        manualEvent.Set();
47
    }
48

49    void CalculateFirstTerm(object stateInfo)
50
    {
51        double preCalc =
randomGenerator.NextDouble();
52        //等待手动重置事件终止

53        manualEvent.WaitOne();       
54        firstTerm = preCalc * baseNumber *
randomGenerator.NextDouble();
55        //置自动事件终止信号

56        autoEvents[0].Set();
57
    }
58

59    void CalculateSecondTerm(object stateInfo)
60
    {
61        double preCalc =
randomGenerator.NextDouble();
62        //等待手动重置事件终止

63        manualEvent.WaitOne();
64        secondTerm = preCalc * baseNumber *
randomGenerator.NextDouble();
65        //置自动事件终止信号

66        autoEvents[1].Set();
67
    }
68

69    void CalculateThirdTerm(object stateInfo)
70
    {
71        double preCalc =
randomGenerator.NextDouble();
72        //等待手动重置事件终止

73        manualEvent.WaitOne();
74        thirdTerm = preCalc * baseNumber *
randomGenerator.NextDouble();
75        //置自动事件终止信号

76        autoEvents[2].Set();
77
    }
78

79    public double Result(int seed)
80
    {
81        randomGenerator = new
Random(seed);
82

83        //将待执行工作加入线程
84        ThreadPool.QueueUserWorkItem(new WaitCallback(CalculateBase));
85        ThreadPool.QueueUserWorkItem(new
WaitCallback(CalculateFirstTerm));
86        ThreadPool.QueueUserWorkItem(new
WaitCallback(CalculateSecondTerm));
87        ThreadPool.QueueUserWorkItem(new
WaitCallback(CalculateThirdTerm));
88

89        //等待所有自动事件终止
90        WaitHandle.WaitAll(autoEvents);
91        //重置手动事件为非终止状态

92        manualEvent.Reset();
93

94        return firstTerm + secondTerm + thirdTerm;
95
    }
96}

 

 

 该示例一共有4个ResetEvent,一个ManualEvent,三个AutoResetEvent,分别反映4个线程的运行状态。

  ManualEvent和AutoResetEvent有一点不同:

  AutoResetEvent是在当前线程调用set方法激活某线程之后,AutoResetEvent状态自动重置;ManualEvent则需要手动调用Reset方法来重置状态。

  接着来看看上面那段代码的执行顺序,Main方法首先调用的是Result 方法,Result方法开启4个线程分别去执行,主线程阻塞在WaitHandle.WaitAll(autoEvents)处,等待3个计算步骤的完成。4个ResetEvent初始化状态都是非终止(构造实例时传入了false),CalculateBase首先执行完毕,其他3个线程阻塞在manualEvent.WaitOne()处,等待CalculateBase执行完成。CalculateBase生成baseNumber后,把代表自己的ManualEvent状态设置为终止状态。其他几个线程从manualEvent.WaitOne()处恢复执行,在执行完自己的代码后把自己对应的AutoResetEvent状态置为终止。当3个计算步骤执行完后,主线程从阻塞中恢复,把三个计算结果累加后返回。

  还要多补充一点的是WaitHandle的WaitAll、WaitAny方法。如果等待多个事件终止就用WaitAll,如本例中的:WaitHandle.WaitAll(autoEvents)。WaitAny是等待的事件数组中有一个为终止状态则停止等待。

posted on 2011-08-09 11:26  罗里罗嗦夫斯基  阅读(468)  评论(0编辑  收藏  举报