对AutoResetEvent和ManualResetEvent的理解

对AutoResetEvent和ManualResetEvent的理解一、作用AutoResetEvent和ManualResetEvent可用于控制线程暂停或继续,拥有重要的三个方法:WaitOne、Set和Reset。这三个方法的官方定义并不好理解,什么终止、非终止,乱七八糟的。在这里,我们以一种通俗易懂的概念来说明。 二、比喻如果把每个线程比作一辆汽车的话,AutoResetEvent和ManualResetEvent就

大家好,又见面了,我是你们的朋友全栈君。

一、作用

AutoResetEvent和ManualResetEvent可用于控制线程暂停或继续,拥有重要的三个方法:WaitOneSetReset

这三个方法的官方定义并不好理解,什么终止、非终止,乱七八糟的。在这里,我们以一种通俗易懂的概念来说明。

 

二、比喻

如果把每个线程比作一辆汽车的话,AutoResetEventManualResetEvent就是公路上的收费站。

其中:

Reset 关闭收费站车闸禁止通行(拦截车辆才好收费啊);

WaitOne 收费员等待下一辆车辆过来(然后收费);

Set    开启收费站车闸放行(交钱了就让过去)。

 

三、AutoResetEvent和ManualResetEvent的区别

既然AutoResetEventManualResetEvent都是收费站,那么它们之间有什么不同之处吗?

顾名思义,Auto即自动,Manual即手动,而Reset根据上面的比喻表示关闭车闸,也就是前者可自动关闭车闸,后者需手动关闭车闸。

自动关闭车闸:即一辆车交钱通过后,车闸会自动关闭,然后再等待下一辆车过来交费。即每辆车都要经过这么几个步骤:被阻 > 交费 > 通行 > 车闸关闭

手动关闭车闸:车闸打开后,车闸不会自动关闭,如果不手动关闭车闸(即调用ManualResetEvent.Reset()方法)的话,车辆会一辆接一辆地通过……

 

所以WaitOne收费操作取决于车闸是否关闭(Reset),如果车闸是开启的,WaitOne的收费愿望只能落空,收费站形同虚设。

 

四、AutoResetEvent和ManualResetEvent的初始状态

通过设置AutoResetEventManualResetEvent构造函数可初始化收费站车闸状态:

new Auto/ManualResetEvent(false)车闸默认关闭;
new Auto/ManualResetEvent(true) 车闸默认开启。

如果new Auto/ManualResetEvent(true),即车闸默认开启的话,WaitOne没任何意义,车辆该通过还通过。

看下面代码:

复制代码
        static EventWaitHandle _tollStation = new AutoResetEvent(true);//车闸默认开启

        static void Main(string[] args)
        {
            new Thread(Car1).Start();
            Console.ReadKey();
        }

        static void Car1()
        {
            _tollStation.WaitOne();//因车闸默认开启,WaitOne毫无意义,不会阻止车辆前行
            Console.WriteLine("噫!车闸是开的,我过来了!");
        }
复制代码

运行将打印:

噫!车闸是开的,我过来了!

如果将new AutoResetEvent(true) 改为new AutoResetEvent(flase),即车闸默认为关闭状态的话,将不会打印任何值,即车辆无法通过。

那如何才能通过呢?必须在主线程中调用Set方法,即打开车闸即可通过。

代码:

 

复制代码
        static EventWaitHandle _tollStation = new AutoResetEvent(false);//车闸默认关闭

        static void Main(string[] args)
        {
            new Thread(Car1).Start();
            _tollStation.Set();//开启车闸
            Console.ReadKey();
        }

        static void Car1()
        {
            _tollStation.WaitOne();//等待开启车闸,即_event.Set();
            Console.WriteLine("车闸开启,我过来了!");
        }
复制代码

运行将打印:

车闸开启,我过来了!

代码很明了,就不解释了,总之就是车闸默认关闭状态下,只有打开车闸(调用Set方法 ),车辆才能通行。

 

五、用代码阐释AutoResetEvent的特性

代码:

复制代码
        static EventWaitHandle _tollStation = new AutoResetEvent(false);//车闸默认关闭

        static void Main(string[] args)
        {
            new Thread(Car1).Start();//车辆1
            new Thread(Car2).Start();//车辆2
            _tollStation.Set();
            Console.ReadKey();
        }

        static void Car1()
        {
            _tollStation.WaitOne();//等待开启车闸,即_tollStation.Set();
            Console.WriteLine("车辆1,顺利通过。");
        }

        static void Car2()
        {
            _tollStation.WaitOne();
            Console.WriteLine("车辆2,顺利通过。!");
        }
复制代码

运行将打印:

车辆1,顺利通过。

虽然车辆1和车辆2都在运行,但只有车辆1顺利通过。

因为_tollStation.Set()仅运行了一次,即车辆1通过后车闸被立即关闭,导致车辆2未被通过。

除非,在车辆1通过后再调用一次_tollStation.Set(),即再次打开车闸,车辆2才能通过:

复制代码
        static EventWaitHandle _tollStation = new AutoResetEvent(false);//车闸默认关闭

        static void Main(string[] args)
        {
            new Thread(Car1).Start();//车辆1
            new Thread(Car2).Start();//车辆2
            _tollStation.Set();//开启车闸,让车辆1通过
            Console.ReadKey();
        }

        static void Car1()
        {
            _tollStation.WaitOne();//等待开启车闸,即_tollStation.Set();
            Console.WriteLine("车辆1,顺利通过。");
            _tollStation.Set();//再开启一次车闸,让车辆2通过
        }

        static void Car2()
        {
            _tollStation.WaitOne();
            Console.WriteLine("车辆2,顺利通过。");
        }
复制代码

运行将打印:

车辆1,顺利通过。

车辆2,顺利通过。

也就是每调用一次Set,仅有一个线程会继续。换言之,有多少个线程就要调用多少次Set,线程才会全部继续。

这也表明,AutoResetEvent是典型的队列操作形式。

 

六、用代码阐释ManualResetEvent的特性

在上一个代码块中,_tollStation.Set()调用了两次,两辆车才顺利通过。

那么,有没有什么办法,只调用一次_tollStation.Set()就让两辆或更多辆汽车顺利通过呢?

答案是,将AutoResetEvent改为ManualResetEvent

复制代码
        static EventWaitHandle _tollStation = new ManualResetEvent(false);//改为ManualResetEvent,车闸默认关闭

        static void Main(string[] args)
        {
            new Thread(Car1).Start();//车辆1
            new Thread(Car2).Start();//车辆2
            _tollStation.Set();//开启车闸,所有车辆都会通过
            Console.ReadKey();
        }

        static void Car1()
        {
            _tollStation.WaitOne();//等待开启车闸,即_tollStation.Set();
            Console.WriteLine("车辆1,顺利通过。");
            //_tollStation.Set();//这里不再需要了
        }

        static void Car2()
        {
            _tollStation.WaitOne();
            Console.WriteLine("车辆2,顺利通过。");
        }
复制代码

 

运行将打印:

车辆1,顺利通过。

车辆2,顺利通过。

这很好的说明了,ManualResetEvent开启车闸后不会自动关闭这一特性。所以调用一次_tollStation.Set(),全部车辆将顺利通过。

如果在某一时刻手动关闭了车闸,则后面的车辆将无法通过。如以下代码:

复制代码
 static EventWaitHandle _tollStation = new ManualResetEvent(false);//改为ManualResetEvent,车闸默认关闭

        static void Main(string[] args)
        {
            new Thread(Car1).Start();//车辆1
            new Thread(Car2).Start();//车辆2

            _tollStation.Set();//开启车闸,放行
            Timer timer = new Timer(CloseDoor, null, 0, 2000);//2秒后关闭车闸

            Console.ReadKey();
        }

        static void Car1()
        {
            _tollStation.WaitOne();//等待开启车闸,即_tollStation.Set();
            Console.WriteLine("车辆1,顺利通过。");
        }

        static void Car2()
        {
            Thread.Sleep(3000);//睡眠3秒
            _tollStation.WaitOne();//当醒来后车闸已经被关闭
            Console.WriteLine("车辆2,顺利通过。");//所以车辆2不会被通过
        }

        /// <summary>
        /// 2秒后关闭车闸
        /// </summary>
        static void CloseDoor(object o)
        {
            _tollStation.Reset();//关闭车闸
        }
复制代码

运行将打印:

车辆1,顺利通过。

而车辆2将不会通过,因为当车辆2醒来时,车闸在2秒前已被关闭。

 

七、总结

1、看起来,ManualResetEvent更加自由、开放。如果把AutoResetEvent看作是只能单人通过的独木桥的话,那么ManualResetEvent就像一座城门,一下子可以涌入千军万马,当然你也可以随时关闭城门,让后面的人进不来。

2、AutoResetEvent.Set() = ManualResetEvent.Set() + ManualResetEvent.Reset();

3、如果共享资源仅允许一个线程单独使用的情况下,可以选择AutoResetEvent;如果共享资源允许多个线程同时使用,则可以选择ManualResetEvent

4、如果要控制多个线程暂停、继续,可以选择ManualResetEvent

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/161175.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • springboot 上传文件设置文件大小限制

    springboot 上传文件设置文件大小限制报错内容:org.springframework.web.multipart.MaxUploadSizeExceededException:Maximumuploadsizeexceeded;nestedexceptionisjava.lang.IllegalStateException:org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException:therequestwasrejectedbe…

    2022年5月29日
    38
  • eclipse 导入父子工程_eclipse 导入maven 父子项目

    eclipse 导入父子工程_eclipse 导入maven 父子项目你先要确认svn上是否是maven项目,否则要自己重新建一个maven项目然后直接引入目录了。如果确认是maven项目,那么有个两个方案。案一:先用任何client软件将svn下载。然后在eclipse选择import,然后当作existmavenproject导入。案二:在project中有checkoutmavenfromscm。scm就是指版本控制软件。不过不同版本控制的sc…

    2022年5月6日
    243
  • servlet和jsp中的Request转发,重定向有何区别_jsp重定向和转发的区别

    servlet和jsp中的Request转发,重定向有何区别_jsp重定向和转发的区别转发request.getRequestDispatcher("/index.jsp").forward(request,response);System.out.println("被执行了");index.jsp&lt;html&gt;&lt;body&gt;&lt;h2&gt;HelloWorld!&lt;/h2&gt;&lt;/body

    2022年9月7日
    0
  • Android preference_android studio preview

    Android preference_android studio preview以前我们经常使用PreferenceActivity来显示给用户一个选项设置的界面,而现在Google推荐用PreferenceFragment来代替使用.先来看看官方文档的描述:Thepreferencehierarchycanbeformedinmultipleways:FromanXMLfilespecifyingthehierarchyFro

    2022年9月3日
    4
  • ubuntu16.04重启网络服务命令_ubuntu重启服务器命令

    ubuntu16.04重启网络服务命令_ubuntu重启服务器命令在更改网络配置后,我们需要重启网络服务使更改生效。在centos系统中,我们可以通过以下命令重启网络服务:/etc/init.d/networkrestart#orservicenetworkrestartUbuntu14.04重启网络服务无效:servicenetworkingrestartstop:Jobfailedwhilestoppingstart:Jobi

    2022年10月9日
    0
  • winform界面美化

    winform界面美化 介绍如下:1.这是DELPHI的换肤软件的DOTNET版,使用简单并有皮肤编辑工具,可到官方网站去下,当前版本为:1.20.1,更新时间:2008-04-202.这次发布的版本包括了DotNetSkin的ForVS2003和VS2005两个版本。3.未破解前,如果要显示标题栏,则标题标显示的是Logo图片,样式请看官方的DEMO程序;如果不显示标题栏,则在程序启动的时候提示一个对话框(T

    2022年5月8日
    41

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号