博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
再谈AutoResetEvent和ManualResetEvent 之详细解说
阅读量:5325 次
发布时间:2019-06-14

本文共 5659 字,大约阅读时间需要 18 分钟。

一,理论部分

      ManualResetEvent和AutoResetEvent主要负责多线程编程中的线程同步;以下一段是引述网上和MSDN的解析及修改说明:在.Net多线程编程中,AutoResetEvent和ManualResetEvent这两个类经常用到, 他们的用法很类似,但也有区别。他们都含有Set()和Reset()方法,解释:Set方法将信号置为发送状态,Reset方法将信号置为不发送状态,WaitOne等待信号的发送。可以通过构造函数的参数值来决定其初始状态,若为true则非阻塞状态,为false为阻塞状态。分别对应事件的终止状态和非终止状态。白话就是说:当为初始状态为true的时候,事件处于终止状态中(也可以说该事件结束了,没有意义了),你再用_autoResetEvent.WaitOne()是不会起到阻滞工作线程的作用的。(PS:ManualResetEvent也同样),反之成立。

      谈它们的构造函数为false时(线程可以起到阻塞状态),如果某个线程调用WaitOne方法,则当信号处于发送状态(Set)时,该线程会得到信号, 继续向下执行。其区别就在调用后,AutoResetEvent.WaitOne()每次只允许一个线程进入,当某个线程得到信号后,AutoResetEvent会自动又将信号置为不发送状态,则其他调用WaitOne的线程只有继续等待.也就是说,AutoResetEvent一次只唤醒一个线程;而ManualResetEvent则可以唤醒多个线程,因为当某个线程调用了ManualResetEvent.Set()方法后,其他调用WaitOne的线程获得信号得以继续执行,而ManualResetEvent不会自动将信号置为不发送。也就是说,除非手工调用了ManualResetEvent.Reset()方法,则ManualResetEvent将一直保持有信号状态,ManualResetEvent也就可以同时唤醒多个线程继续执行。从函数的名称就可以看出这个意思Manual:手动;Auto:自动,不用死记,一看便知。

本质上AutoResetEvent.Set()方法相当于ManualResetEvent.Set()+ManualResetEvent.Reset(); 信号会在有无之间自动切换

因此AutoResetEvent一次只能唤醒一个线程,其他线程还是堵塞。

二 ,实例部分

 

实例1:ManualResetEvent  用一个三国演义的典故来写段示例代码:()

 

话说曹操率领80W大军准备围剿刘备和孙权,面对敌众我寡的情况,诸葛亮与周瑜想到了一个妙计,用装满火药桶的大船去冲击曹操连在一起的战船,计划都安排好了,可谓“万事俱备 只欠东风”。代码如下

namespace test{    class Program    {        //默认信号为不发送状态        private static ManualResetEvent _manualResetEvent = new ManualResetEvent(false);        static void Main(string[] args)        {            EastWind wind = new EastWind(_manualResetEvent);            //启动东风的线程                Thread thd = new Thread(new ThreadStart(wind.WindComming));            thd.Start();            _manualResetEvent.WaitOne();//万事俱备只欠东风,事情卡在这里了,在东风来之前,诸葛亮没有进攻                 //东风到了,可以进攻了  (得到set()发出的消息后执行)              Console.WriteLine("诸葛亮大吼:东风来了,可以进攻了,满载燃料的大船接着东风冲向曹操的战船");            Console.ReadLine();        }    }    class EastWind    {        ManualResetEvent _mre;        public EastWind(ManualResetEvent mre)        {            _mre = mre;        }        ///          /// 风正在吹过来         ///          public void WindComming()        {            Console.WriteLine("东风正在吹过来");            for (int i = 0; i <= 5; i++)            {                Thread.Sleep(500);                Console.WriteLine("东风吹啊吹,越来越近了...");            }            Console.WriteLine("东风终于到了");            //通知诸葛亮东风已到,可以进攻了, 发送信号通知阻塞的线程可以继续执行了                _mre.Set();        }    }  }

 运行结果:

2,实例二 比较AutoResetEvent和ManualResetEvent的区别

代码片段1:

        AutoResetEvent _autoResetEvent = new AutoResetEvent(false);

        private void BT_Temp_Click(object sender, RoutedEventArgs e)

        {
            Thread t1 = new Thread(this.Thread1Foo);
            t1.Start();
            Thread.Sleep(3000);
            _autoResetEvent.Set();
        }

        void Thread1Foo()

        {
            _autoResetEvent.WaitOne();
            MessageBox.Show("t1 end");
        }
    这段代码的执行结果,就是3秒钟过后,弹出“t1 end”。
    而如果把:
    AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
    改为:
    AutoResetEvent _autoResetEvent = new AutoResetEvent(true);
    则“t1 end”将会立刻弹出。
    也就是说,在终止状态中,_autoResetEvent.WaitOne()是不会起到阻滞工作线程的作用的。(PS:ManualResetEvent也同样)

二:AutoResetEvent和ManualResetEvent的区别

    接下来,再来看看AutoResetEvent和ManualResetEvent的区别。我们看代码段2和代码段3:

代码段2:

     AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
        private void BT_Temp_Click(object sender, RoutedEventArgs e)
        {
            Thread t1 = new Thread(this.Thread1Foo);
            t1.Start();
            Thread t2 = new Thread(this.Thread2Foo);
            t2.Start();
            Thread.Sleep(3000);
            _autoResetEvent.Set();
        }

        void Thread1Foo()

        {
            _autoResetEvent.WaitOne();
            MessageBox.Show("t1 end");
        }

        void Thread2Foo()

        {
            _autoResetEvent.WaitOne();
            MessageBox.Show("t2 end");
        }
    该段代码运行的效果是,过3秒后,要么弹出“t1 end”,要么弹出“t2 end”,不会两个都弹出。也就是说,其中一个进行将会结束,而另一个进程永远不会结束。

代码段3:

        ManualResetEvent _menuRestEvent = new ManualResetEvent(false);

        private void BT_Temp_Click(object sender, RoutedEventArgs e)

        {
            Thread t1 = new Thread(this.Thread1Foo);
            t1.Start();
            Thread t2 = new Thread(this.Thread2Foo);
            t2.Start();
            Thread.Sleep(3000);
            _menuRestEvent.Set();
        }

        void Thread1Foo()

        {
            _menuRestEvent.WaitOne();
            MessageBox.Show("t1 end");
        }

        void Thread2Foo()

        {
            _menuRestEvent.WaitOne();
            MessageBox.Show("t2 end");
        }
    该段代码运行的效果是,过3秒后,“t1 end”和“t2 end”,两个都被弹出。也就是说,两个进程都结束了。
    这个特性就是说,AutoResetEvent只会给一个线程发送信号,而不会给多个线程发送信号。在我们需要同步多个线程的时候,就只能采用ManualResetEvent了。至于深层次的原因是,AutoResetEvent在set()之后,会将线程状态自动置为false,而ManualResetEvent在Set()后,线程的状态就变为true了,必须手动ReSet()之后,才会重新将线程置为false。这也就是为什么他们的名字一个为Auto,一个为Manual的原因。为了更加充分的验证ManualResetEvent的这点特性,我们再来看代码片段4

代码片段4:

    ManualResetEvent _menuRestEvent = new ManualResetEvent(false);

        private void BT_Temp_Click(object sender, RoutedEventArgs e)

        {
            Thread t1 = new Thread(this.Thread1Foo);
            t1.Start();
            Thread t2 = new Thread(this.Thread2Foo);
            t2.Start();
            Thread.Sleep(3000);
            _menuRestEvent.Set();
            //_menuRestEvent.Reset();           
        }

        void Thread1Foo()

        {
            _menuRestEvent.WaitOne();
            MessageBox.Show("t1 step1 end");
            //睡1S,用于等待主线程_menuRestEvent.Reset();
            Thread.Sleep(1000);
            _menuRestEvent.WaitOne();
            MessageBox.Show("t1 step2 end");
        }

        void Thread2Foo()

        {
            _menuRestEvent.WaitOne();
            MessageBox.Show("t2 step1 end");
            //睡1S,用于等待主线程_menuRestEvent.Reset();
            Thread.Sleep(1000);
            _menuRestEvent.WaitOne();
            MessageBox.Show("t2 step2 end");
        }
    在代码片段4中,我们对//_menuRestEvent.Reset()进行了注释,也就是说, _menuRestEvent.Set()后,线程的状态就是true状态的,程序运行的结果是"t1 step1 end"、"t1 step2 end"、"t1 step2 end"、"t2 step2 end"在3秒之后全部弹出。
    而如果我们将//_menuRestEvent.Reset()的注释去掉,会发现t1 step1 end和t2 step1 end出现其中一个而"t1 step2 end"和"t2 step2 end"永远不会弹出。除非我们在主线程中再次对_menuRestEvent进行Set()。

 

三,注意点

另外WaitOne方法也可以带两个参数:WaitOne (int millisecondsTimeout,bool exitContext)

millisecondsTimeout:等待的毫秒数,或为 Timeout.Infinite (-1),表示无限期等待。
exitContext:为 true,则等待之前先退出上下文的同步域(如果在同步上下文中),然后在稍后重新获取它;否则为false。
就是说,等待是可以加上一个期限的,如果等待的同步对象一直都不Set()的话,那么程序就会卡死,所以在WaitOne方法里面可以放置一个时间期限,单位是毫秒。防止程序死了。

转载于:https://www.cnblogs.com/mistor/archive/2013/04/03/2998436.html

你可能感兴趣的文章
css文本溢出显示省略号
查看>>
git安装和简单配置
查看>>
面向对象:反射,双下方法
查看>>
鼠标悬停提示文本消息最简单的做法
查看>>
课后作业-阅读任务-阅读提问-2
查看>>
面向对象设计中private,public,protected的访问控制原则及静态代码块的初始化顺序...
查看>>
fat32转ntfs ,Win7系统提示对于目标文件系统文件过大解决教程
查看>>
Awesome Adb——一份超全超详细的 ADB 用法大全
查看>>
shell cat 合并文件,合并数据库sql文件
查看>>
Android 将drawable下的图片转换成bitmap、Drawable
查看>>
介绍Win7 win8 上Java环境的配置
查看>>
移动、联通和电信,哪家的宽带好,看完你就知道该怎么选了!
查看>>
Linux设置环境变量的方法
查看>>
构建自己的项目管理方案
查看>>
利用pca分析fmri的生理噪声
查看>>
div水平居中且垂直居中
查看>>
epoll使用具体解释(精髓)
查看>>
AndroidArchitecture
查看>>
安装Endnote X6,但Word插件显示的总是Endnote Web"解决办法
查看>>
python全栈 计算机硬件管理 —— 硬件
查看>>