与互斥和信号量对象一样,事件也是一个系统范围内的资源同步方法。为了从托管代码中使用系统事件,.NET Framework在System.Threading名称空间中提供了ManualResetEvent、AutoResetEvent、ManualResetEventSlim和CountdownEvent类。

注意:

第8章介绍了C#中event关键字,它与System.Threading名称空间中的event类没有关系。event关键字基于委托,而上述event类是.NET封装类,用于系统范围内的本机事件资源的同步。

可以使用事件通知其他任务:这里有一些数据,并完成了一些操作等。事件可以发信号, 也可以不发信号。使用前面介绍的WaitHandle类,任务可以等待处于发信号状态的事件。

调用Set()方法,即可向ManualResetEventSlim发信号。调用Reset()方法,可以使之返回不发信号的状态。如果多个线程等待向一个事件发信号,并调用了Set()方法,就释放所有等待的线程。另外,如果一个线程刚刚调用了WaitOne()方法,但事件已经发出信号,等待的线程就可以继续等待。

也通过调用Set()方法向AutoResetEvent发信号。也可以使用Reset()方法使之返回不发信号的状态。但是,如果一个线程在等待AutoResetEvent发信号,当第一个线程的等待状态结束时,该事件会自动变为不发信号的状态。这样,如果多个线程在等待向事件发信号,就只有一个线程结束其等待状态,它不是等待时间最长的线程,而是优先级最高的线程。

为了说明ManualResetEventSlim类的事件,下面的Calculator类定义了Calculation()方法,这是任务的入口点。在这个方法中,该任务接收用于计算的输入数据,将结果写入变量result,该变量可以通过Result属性来访问。只要完成了计算(在随机的一段事件后),就调用ManualResetEventSlim类的Set方法,向事件发信号。

    class Calculator{private ManualResetEventSlim _mEvent;public int Result { get; private set; }public Calculator(ManualResetEventSlim ev){_mEvent = ev;}public void Calculation(int x,int y){Console.WriteLine($"Task {Task.CurrentId} starts calculation");Task.Delay(new Random().Next(3000)).Wait();Result = x + y;//signal the event-completed!Console.WriteLine($"Task {Task.CurrentId} is ready");_mEvent.Set();}}

程序的Main()方法定义了包含4个ManualResetEventSlim对象的数组和包含4个Calculator对象的数组。每个Calculator在构造函数中用一个ManualResetEventSlim对象的初始化,这样每个任务在完成时都有自己的事件对象来发信号。现在使用Task类,让不同的任务执行计算任务。

        static void Main(string[] args){const int taskCount = 4;var mEvents = new ManualResetEventSlim[taskCount];var waitHandles = new WaitHandle[taskCount];var calcs = new Calculator[taskCount];for (int i = 0; i < taskCount; i++){int i1 = i;mEvents[i] = new ManualResetEventSlim(false);waitHandles[i] = mEvents[i].WaitHandle;calcs[i] = new Calculator(mEvents[i]);Task.Run(() => calcs[i].Calculation(i1+1,i1+3)) ;}}

WaitHandle类现在用于等待数组中的任意一个事件。WaitAny()方法等待向任意一个事件发信号。与ManualResetEvent对象不同,ManualResetEventSlim对象不派生自WaitHandle类。因此有一个WaitHandle对象的结合,它在ManualResetEventSlim类的WaitHandle属性中填充。从WaitAny()方法返回的index值匹配传递给WaitAny()方法的事件数组的索引,以提供发信号的事件的相关信息,使用该索引可以从这个事件中读取结果。

            for (int i = 0; i < taskCount; i++){int index = WaitHandle.WaitAny(waitHandles);if (index == WaitHandle.WaitTimeout){WriteLine("Timeout!!");}else{mEvents[index].Reset();WriteLine($"finished task for {index}, result: {calcs[index].Result}");}}}

启动应用程序时,可以看到任务在进行计算并设置事件,以通知主线程,它可以读取结果了。在任意时间,依据是调试版本还是发布版本,以及硬件的不同,会看到执行调用的任务有不同的顺序和不同的数量。

Task 3 starts calculation
Task 1 starts calculation
Task 2 starts calculation
Task 4 starts calculation
Task 3 is ready
finished task for 2, result: 8
Task 4 is ready
finished task for 3, result: 10
Task 1 is ready
finished task for 0, result: 4
Task 2 is ready
finished task for 1, result: 6

在一个类似的场景中,为了把一些工作分支到多个任务中,并在以后合并结果,使用新的CountdownEvent类很有用。不需要为每个任务创建一个单独的事件对象,而只需要创建一个事件对象。CountdownEvent类为所有设置了事件的任务定义一个初始数字,在到达该计数后,就向CountdownEvent类发信号。

修改Calculator类,以使用CountdownEvent类替代ManualResetEvent类。不使用Set()方法设置信号,而使用CountdownEvent类定义Signal()方法。

    class Calculator{private CountdownEvent _cEvent;public int Result { get; private set; }public Calculator(CountdownEvent ev){_cEvent = ev;}public void Calculation(int x,int y){WriteLine($"Task {Task.CurrentId} starts calculation");Task.Delay(new Random().Next(3000)).Wait();Result = x + y;//signal the event-completed!WriteLine($"Task {Task.CurrentId} is ready");_cEvent.Signal();}}

Main()方法现在可以简化,使它只需要等待一个事件。如果不像前面那样单独处理结果,这个新版本就很不错。

        static void Main(string[] args){const int taskCount = 4;var cEvent = new CountdownEvent(taskCount);var calcs = new Calculator[taskCount];for (int i = 0; i < taskCount; i++){int i1 = i;calcs[i] = new Calculator(cEvent);Task.Run(() => calcs[i1].Calculation(i1 + 1, i1 + 3));}cEvent.Wait();WriteLine("all finished");for (int i = 0; i < taskCount; i++){WriteLine($"finished task for {i}, result: {calcs[i].Result}");}}}

运行结果:

Task 3 starts calculation
Task 4 starts calculation
Task 2 starts calculation
Task 1 starts calculation
Task 3 is ready
Task 4 is ready
Task 1 is ready
Task 2 is ready
all finished
finished task for 0, result: 4
finished task for 1, result: 6
finished task for 2, result: 8
finished task for 3, result: 10

十五、Events类相关推荐

  1. C++学习 十五、类继承(1)基类,派生类,访问权限,protected

    C++学习 十五.类继承(1)基类,派生类 前言 类继承 类的关系与继承 基类, 派生类 基类 派生类 构造函数,析构函数 文件位置 访问权限 protected 后记 前言 本篇开始学习C++类的继 ...

  2. 类的成员包含 java_Java 面向对象(十五)类的成员 之 内部类

    一.内部类 1.引入 类的成员包括: (1)属性:成员变量: (2)方法:成员方法: (3)构造器: (4)代码块: (5)内部类: 其中1.2是代表这类事物的特征: 其中3.4是初始化类和对象用的: ...

  3. 十五、类与封装的概念

    1.类的封装 C++中类的封装: 成员变量:C++中用于表示类属性的变量 成员函数:C++中用于表示类行为的函数 C++中可以给成员变量和成员函数定义访问级别 public:成员变量和成员函数可以在类 ...

  4. C#OOP之十五 String类StringBuilder类

    String类的概述 初次看到String这个词,大家应该都很熟悉.有的同学就会想到:它不就是C#中的一个关键字吗?我们已经接触过了啊,它就是一个数据类型而已,能有何大用?也有细心的同学会发现:这个S ...

  5. java程序员从笨鸟到菜鸟之(十五)StringBuffer类练习题

    实例1: package org.westos.stringbuffer_02; /*** 简答题:* String,StringBuffer,StringBuilder的区别?* 从可变性:* St ...

  6. CorelDRAWX4的VBA插件开发(二十五)多类目选择实现复合框内容置换

    有时候我们的复合框内的内容是有更多的需求,但是我们又不想另开一个窗口,怎么办呢,我们直接使用复合框内容替换就行啦 先上效果图: 先在面第板上拉出一个框架, 再在里面添加上三个选项按钮, 添加好之后这三 ...

  7. CorelDRAWX4的VBA插件开发(四十五)建立类(2)汇总相似功能简化重复代码:一键建立设计外框加出血线和等分折页线

    在上一节中已经建立好了类,那么这一节我们来调用它,先建立一个面板 然后修改框体名称 然后从左侧新建一些按钮并且以拼音为结尾进行命名 Private Sub CheckBox2_zheYe_Click( ...

  8. Python学习日记(二十五) 接口类、抽象类、多态

    接口类 继承有两种用途:继承基类的方法,并且做出自己的改变或扩展(代码重用)和声明某个子类兼容于某基类,定义一个接口类interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子 ...

  9. J2EE进阶(十五)MyEclipse反向工程实现从数据库反向生成实体类之Hibernate方式[申明:来源于网络]...

    J2EE进阶(十五)MyEclipse反向工程实现从数据库反向生成实体类之Hibernate方式[申明:来源于网络] 地址:http://blog.csdn.net/sunhuaqiang1/arti ...

  10. 【零基础学Java】—Socket类(五十五)

    [零基础学Java]-Socket类(五十五) Socket类:该类实现客户端套接字,套接字是指两台设备之间通讯的端点. 在Java中,提供了两个类用于实现TCP通信程序 客户端:java.net.S ...

最新文章

  1. 七、使用栈实现综合计算器(中缀表达式)
  2. vim在退出时,处理隐藏缓冲区的方式
  3. 2.2 Logistic 回归-深度学习-Stanford吴恩达教授
  4. word 常用快捷键
  5. Apache Flink 零基础入门(十四)Flink 分布式缓存
  6. 百思不得姐第4天:文本框占位文字颜色
  7. iOS开发人员不容错过的10大工具
  8. 求1到n ,这n个整数的二进制表示比特1的个数(时间复杂度:O(n))
  9. python哪些系统可以运行_python可以检测它在哪个操作系统下运行吗?
  10. Vue- Markdown 使用大全
  11. 编译用户Orcle的package中访问其它Schema的表
  12. DevExpress 小结
  13. 最详细的Java入门完整教程,学Java先收藏了!
  14. 勤哲服务器项目模板,勤哲excel服务器,模板.doc
  15. 基于单片机的无线病房呼叫系统设计
  16. MAK代理激活的使用方法和注意事项
  17. 微信访问时,提示:该地址为ip地址,请使用域名访问网站
  18. Pycharm导入tabula模块包
  19. 莫队-一个让查询的高效的方法-并不深刻的讲解文章-但是易懂!
  20. UFI模式下GPT分区安装win8

热门文章

  1. Swiper.js实现无缝滚动
  2. AI经典书单:入门人工智能该读哪些书?
  3. 『outlaws』西部狂徒服务器架设
  4. 数量X金额=总额 再例如X抽成0.08等出抽成金额180,大师们帮忙改一下下,万分感谢!
  5. 中国机器视觉产业全景图谱
  6. LeetCode刷题|36有效的数独
  7. 【AIOT】2-2 物联网案例分享
  8. 工作经验这样写,面试就有了!
  9. 2018年315晚会黑名单之夜,谁能逃过此劫?
  10. 做一个网站真的有那么难吗?