对AutoResetEvent和ManualResetEvent的理解

2024-01-15 04:32

本文主要是介绍对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();//车辆1new 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();//车辆1new 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();//车辆1new 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();//车辆1new 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

这篇关于对AutoResetEvent和ManualResetEvent的理解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/607650

相关文章

深入理解Apache Kafka(分布式流处理平台)

《深入理解ApacheKafka(分布式流处理平台)》ApacheKafka作为现代分布式系统中的核心中间件,为构建高吞吐量、低延迟的数据管道提供了强大支持,本文将深入探讨Kafka的核心概念、架构... 目录引言一、Apache Kafka概述1.1 什么是Kafka?1.2 Kafka的核心概念二、Ka

深入理解Apache Airflow 调度器(最新推荐)

《深入理解ApacheAirflow调度器(最新推荐)》ApacheAirflow调度器是数据管道管理系统的关键组件,负责编排dag中任务的执行,通过理解调度器的角色和工作方式,正确配置调度器,并... 目录什么是Airflow 调度器?Airflow 调度器工作机制配置Airflow调度器调优及优化建议最

一文带你理解Python中import机制与importlib的妙用

《一文带你理解Python中import机制与importlib的妙用》在Python编程的世界里,import语句是开发者最常用的工具之一,它就像一把钥匙,打开了通往各种功能和库的大门,下面就跟随小... 目录一、python import机制概述1.1 import语句的基本用法1.2 模块缓存机制1.

深入理解C语言的void*

《深入理解C语言的void*》本文主要介绍了C语言的void*,包括它的任意性、编译器对void*的类型检查以及需要显式类型转换的规则,具有一定的参考价值,感兴趣的可以了解一下... 目录一、void* 的类型任意性二、编译器对 void* 的类型检查三、需要显式类型转换占用的字节四、总结一、void* 的

深入理解Redis大key的危害及解决方案

《深入理解Redis大key的危害及解决方案》本文主要介绍了深入理解Redis大key的危害及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录一、背景二、什么是大key三、大key评价标准四、大key 产生的原因与场景五、大key影响与危

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念