AutoResetEvent和ManualResetEvent的异同介绍

一般情况下多线程总是无序的,要使其按照一定的顺序执行(比如B线程必须在A线程执行完毕之后才执行)。可以采用调用Thread的Join方式,也可以使用“信号”类的方式完成此任务。常见的信号类有AutoResetEvent和ManualResetEvent。一般情况下,可以给它们的默认构造函数传入false,这就表示当前初始化该信号源准备发送信号,当调用了WaitOne方法的时候把当前的线程阻塞,直到接受到信号为止(发送信号使用Set函数)。下面是一个例子:

publicclass Example

    {

       
///<summary>

       
/// 预备信号,准备发送,初始化

       
///</summary>

        publicstatic AutoResetEvent flag =new AutoResetEvent(false);



       
publicvoid Begin()

        {

Thread th
=new Thread(() =>

            {

               
for (int i =1; i <6; i++)

                {

                    Thread.Sleep(
1000);

                    Console.WriteLine(
"子线程数据:"+i);

                }

                flag.Set();

            });



            th.IsBackground
=true;

            th.Start();

        }



       
staticvoid Main(string[] args)

        {

            Example e
=new Example();

            e.Begin();

           
//等待该信号接受到完毕,才执行主线程中的任务循环

            flag.WaitOne();

           
for (int i =1; i <6; i++)

            {

                  Thread.Sleep(
1000);

                  Console.WriteLine(
"主线程数据:"+i);

            }

        }

    }
 

上面的例子换成ManualResetEvent亦如此。

那么它们区别在何处呢?其实单从一个线程根本看不出区别。下面我们看两个延时不同的线程同时访问一个AutoResetEvent、ManualResetEvent的例子:

publicclass Example

    {

       
///<summary>

       
/// 预备信号,准备发送,初始化

       
///</summary>

        public AutoResetEvent flag =new AutoResetEvent(false);



       
publicvoid Begin()

        {

            Thread th1
=new Thread(() =>

            {

                Thread.Sleep(
1000);

                Console.WriteLine(
"第一个线程已经通过……");

                flag.Set();

            });



            Thread th2
=new Thread(() =>

            {

                Thread.Sleep(
500);

                Console.WriteLine(
"第二个线程已经通过……");

                flag.Set();

            });



            th1.IsBackground
=true;

            th1.Start();

            th2.IsBackground
=true;

            th2.Start();

            flag.WaitOne();

            flag.WaitOne();

        }



       
staticvoid Main(string[] args)

        {

            Example e
=new Example();

            e.Begin();

        }

    }

因为第一个线程延时1秒,第二个线程延时0.5秒,此时Begin函数中遇到了WaitOne,主线程等待子线程完成任务(因为只有任意一个线程向主线程发送“完成”的信号,即调用Set函数之后主线程方可继续)。因此第一个WaitOne的时候由于第二个线程延时小于第一个线程,因此第二个线程先完成并输出内容;主线程接着又碰到了WaitOne,此时由于第一次的Set已经把AutoResetEvent重新设置为false,所以WaitOne又开始继续等待——直到第一个线程完成为止。 因此我们可以得出一个结论:AutoResetEvent在收到信号之后立即可以再次WaitOne,从而等待第二次、第三次……乃至更多次的Set,每次Set之后AutoResetEvent都会自动初始化为false,为下一次WaitOne做准备

但是ManualResetEvent则不然。如果你把上面的替换成该类的话只输出“第二个线程通过”——究其原因,是因为在第一个WaitOne被Set之后,其内部构造函数的那个“false”已经被替换成了“true”,因此再也不会进行下一轮的Wait。

解决方法是在两个WaitOne之间调用Reset方法——这样使得你的ManualResetEvent在第一次被Set之后初始化成false的状态(预发信号状态),准备第二次的WaitOne。

posted on 2013-04-02 10:52  RIVERSPIRIT  阅读(154)  评论(0)    收藏  举报