C#多线程系列之线程通知

作者:痴者工良 时间:2023-09-13 17:08:03 

AutoRestEvent 类用于从一个线程向另一个线程发送通知。

微软文档是这样介绍的:表示线程同步事件在一个等待线程释放后收到信号时自动重置。

其构造函数只有一个:

构造函数里面的参数用于设置信号状态。

构造函数说明
AutoResetEvent(Boolean)用一个指示是否将初始状态设置为终止的布尔值初始化 AutoResetEvent 类的新实例。

真糟糕的机器翻译。

常用方法

AutoRestEvent 类是干嘛的,构造函数的参数又是干嘛的?不着急,我们来先来看看这个类常用的方法:

方法说明
Close()释放由当前 WaitHandle 占用的所有资源。
Reset()将事件状态设置为非终止,从而导致线程受阻。
Set()将事件状态设置为有信号,从而允许一个或多个等待线程继续执行。
WaitOne()阻止当前线程,直到当前 WaitHandle 收到信号。
WaitOne(Int32)阻止当前线程,直到当前 WaitHandle 收到信号,同时使用 32 位带符号整数指定时间间隔(以毫秒为单位)。
WaitOne(Int32, Boolean)阻止当前线程,直到当前的 WaitHandle 收到信号为止,同时使用 32 位带符号整数指定时间间隔,并指定是否在等待之前退出同步域。
WaitOne(TimeSpan)阻止当前线程,直到当前实例收到信号,同时使用 TimeSpan 指定时间间隔。
WaitOne(TimeSpan, Boolean)阻止当前线程,直到当前实例收到信号为止,同时使用 TimeSpan 指定时间间隔,并指定是否在等待之前退出同步域。

一个简单的示例

这里我们编写一个这样的程序:

创建一个线程,能够执行多个阶段的任务;每完成一个阶段,都需要停下来,等待子线程发生通知,才能继续下一步执行。

.WaitOne() 用来等待另一个线程发送通知;

.Set() 用来对线程发出通知,此时 AutoResetEvent 变成终止状态;

.ReSet() 用来重置 AutoResetEvent 状态;

class Program
   {
       // 线程通知
       private static AutoResetEvent resetEvent = new AutoResetEvent(false);

static void Main(string[] args)
       {
           // 创建线程
           new Thread(DoOne).Start();

// 用于不断向另一个线程发送信号
           while (true)
           {
               Console.ReadKey();
               resetEvent.Set();           // 发生通知,设置终止状态
           }
       }

public static void DoOne()
       {
           Console.WriteLine("等待中,请发出信号允许我运行");

// 等待其它线程发送信号
           resetEvent.WaitOne();

Console.WriteLine("\n     收到信号,继续执行");
           for (int i = 0; i < 5; i++) Thread.Sleep(TimeSpan.FromSeconds(0.5));

resetEvent.Reset(); // 重置为非终止状态
           Console.WriteLine("\n第一阶段运行完毕,请继续给予指示");

// 等待其它线程发送信号
           resetEvent.WaitOne();
           Console.WriteLine("\n     收到信号,继续执行");
           for (int i = 0; i < 5; i++) Thread.Sleep(TimeSpan.FromSeconds(0.5));

Console.WriteLine("\n第二阶段运行完毕,线程结束,请手动关闭窗口");
       }
   }

解释一下

AutoResetEvent 对象有终止和非终止状态。Set() 设置终止状态,Reset() 重置非终止状态。

这个终止状态,可以理解成信号已经通知;非终止状态则是信号还没有通知。

注意,注意终止状态和非终止状态指的是 AutoResetEvent 的状态,不是指线程的状态。

线程通过调用 WaitOne() 方法,等待信号;
另一个线程可以调用 Set() 通知 AutoResetEvent 释放等待线程。
然后 AutoResetEvent 变为终止状态。

需要注意的是,如果 AutoResetEvent 已经处于终止状态,那么线程调用 WaitOne() 不会再起作用。除非调用Reset() 。

构造函数中的参数,正是设置这个状态的。true 代表终止状态,false 代表非终止状态。如果使用 new AutoResetEvent(true); ,则线程一开始是无需等待信号的。

在使用完类型后,您应直接或间接释放类型,显式调用 Close()/Dispose() 或 使用 using。 当然,也可以直接退出程序。

需要注意的是,如果多次调用 Set() 的时间间隔过短,如果第一次 Set() 还没有结束(信号发送需要处理时间),那么第二次 Set() 可能无效(不起作用)。

复杂一点的示例

我们设计一个程序:

  • Two 线程开始处于阻塞状态;

  • 线程 One 可以设置线程 Two 继续运行,然后阻塞自己;

  • 线程 Two 可以设置 One 继续运行,然后阻塞自己;

C#多线程系列之线程通知

程序代码如下(运行后,请将键盘设置成英文输入状态再按下按键):

class Program
   {
       // 控制第一个线程
       // 第一个线程开始时,AutoResetEvent 处于终止状态,无需等待信号
       private static AutoResetEvent oneResetEvent = new AutoResetEvent(true);

// 控制第二个线程
       // 第二个线程开始时,AutoResetEvent 处于非终止状态,需要等待信号
       private static AutoResetEvent twoResetEvent = new AutoResetEvent(false);

static void Main(string[] args)
       {
           new Thread(DoOne).Start();
           new Thread(DoTwo).Start();

Console.ReadKey();
       }

public static void DoOne()
       {
           while (true)
           {
               Console.WriteLine("\n① 按一下键,我就让DoTwo运行");
               Console.ReadKey();
               twoResetEvent.Set();
               oneResetEvent.Reset();
               // 等待 DoTwo() 给我信号
               oneResetEvent.WaitOne();

Console.ForegroundColor = ConsoleColor.Green;
               Console.WriteLine("\n     DoOne() 执行");
               Console.ForegroundColor = ConsoleColor.White;
           }
       }

public static void DoTwo()
       {
           while (true)
           {
               Thread.Sleep(TimeSpan.FromSeconds(1));

// 等待 DoOne() 给我信号
               twoResetEvent.WaitOne();

Console.ForegroundColor = ConsoleColor.Yellow;
               Console.WriteLine("\n     DoTwo() 执行");
               Console.ForegroundColor = ConsoleColor.White;

Console.WriteLine("\n② 按一下键,我就让DoOne运行");
               Console.ReadKey();
               oneResetEvent.Set();
               twoResetEvent.Reset();
           }
       }
   }

C#多线程系列之线程通知

解释

两个线程具有的功能:阻塞自己、解除另一个线程的阻塞。

用电影《最佳拍档》里面的一个画面来理解。

DoOne 、DoTwo 轮流呼吸,不能自己控制自己呼吸,但自己能够决定别人呼吸。

你搞我,我搞你,就能相互呼吸了。

C#多线程系列之线程通知

当然WaitOne() 也可以设置等待时间,如果 光头佬(DoOne) 耍赖不让 金刚(DoTwo)呼吸,金刚等待一定时间后,可以强行荡动天平,落地呼吸。

注意,AutoRestEvent 用得不当容易发生死锁。 
另外 AutoRestEvent 使用的是内核时间模式,因此等待时间不能太长,不然比较耗费 CPU 时间。

AutoResetEvent 也适合用于线程同步。

另外,线程中使用 WaitOne() ,另一个线程使用 Set() 通知后, AutoResetEvent 对象会自动恢复非终止状态,不需要线程使用 Reset() 。

来源:https://www.cnblogs.com/whuanle/p/12730169.html

标签:C#,多线程,线程,通知
0
投稿

猜你喜欢

  • android实现验证码按钮

    2023-06-23 21:30:16
  • android实现打地鼠游戏

    2023-09-25 08:45:59
  • C#读写文本文件的方法

    2022-02-01 15:20:51
  • 深入了解Java ServletContext

    2023-11-08 22:36:27
  • 详解spring-boot集成elasticsearch及其简单应用

    2021-08-26 01:09:58
  • Java受检异常的一些思考

    2021-06-08 08:22:16
  • .net 随机生成汉字

    2022-01-22 08:33:33
  • flutter实现头部tabTop滚动栏

    2022-03-21 14:00:06
  • 理解java和python类变量以及类的成员变量

    2023-09-14 19:42:55
  • 简单仿写Android控件SlidingMenu的实例代码

    2022-01-23 05:11:29
  • Mybatis如何实现@Select等注解动态组合SQL语句

    2022-04-13 16:42:05
  • 解决Android从相册中获取图片出错图片却无法裁剪问题的方法

    2023-09-14 23:41:15
  • 一步步实现Viewpager卡片翻页效果

    2022-10-15 02:14:25
  • c# 常见文件路径Api的使用示例

    2023-06-03 21:06:12
  • Kotlin利用Regex如何构建正则表达式详解

    2022-12-25 18:31:10
  • C#微信公众号开发之服务器配置

    2023-03-12 15:02:50
  • Ajax实现省市区三级联动

    2023-01-14 05:09:58
  • Springboot公共字段填充及ThreadLocal模块改进方案

    2023-11-17 22:58:39
  • Unity 从Resources中动态加载Sprite图片的操作

    2023-08-26 11:37:18
  • struts2 validation.xml 验证规则代码解析

    2021-09-14 22:01:27
  • asp之家 软件编程 m.aspxhome.com