回顾一下,前面 lock、Monitor 部分我们学习了线程锁,Mutex 部分学习了进程同步,Semaphor 部分学习了资源池限制。

这一篇将学习 C# 中用于发送线程通知的 AutoRestEvent 类。

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 继续运行,然后阻塞自己;

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

    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();
}
}
}

解释

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

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

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

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

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

注意,AutoRestEvent 用得不当容易发生死锁。

另外 AutoRestEvent 使用的是内核时间模式,因此等待时间不能太长,不然比较耗费 CPU 时间。

AutoResetEvent 也适合用于线程同步。

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

C#多线程(6):线程通知的更多相关文章

  1. C#多线程之线程池篇3

    在上一篇C#多线程之线程池篇2中,我们主要学习了线程池和并行度以及如何实现取消选项的相关知识.在这一篇中,我们主要学习如何使用等待句柄和超时.使用计时器和使用BackgroundWorker组件的相关 ...

  2. C#多线程之线程池篇2

    在上一篇C#多线程之线程池篇1中,我们主要学习了如何在线程池中调用委托以及如何在线程池中执行异步操作,在这篇中,我们将学习线程池和并行度.实现取消选项的相关知识. 三.线程池和并行度 在这一小节中,我 ...

  3. C#多线程之线程同步篇3

    在上一篇C#多线程之线程同步篇2中,我们主要学习了AutoResetEvent构造.ManualResetEventSlim构造和CountdownEvent构造,在这一篇中,我们将学习Barrier ...

  4. C#多线程之线程同步篇2

    在上一篇C#多线程之线程同步篇1中,我们主要学习了执行基本的原子操作.使用Mutex构造以及SemaphoreSlim构造,在这一篇中我们主要学习如何使用AutoResetEvent构造.Manual ...

  5. Java多线程 3 线程同步

    在之前,已经学习到了线程的创建和状态控制,但是每个线程之间几乎都没有什么太大的联系.可是有的时候,可能存在多个线程多同一个数据进行操作,这样,可能就会引用各种奇怪的问题.现在就来学习多线程对数据访问的 ...

  6. Java学习笔记-多线程-创建线程的方式

    创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...

  7. .net学习之多线程、线程死锁、线程通信 生产者消费者模式、委托的简单使用、GDI(图形设计接口)常用的方法

    1.多线程简单使用(1)进程是不执行代码的,执行代码的是线程,一个进程默认有一个线程(2)线程默认情况下都是前台线程,要所有的前台线程退出以后程序才会退出,进程里默认的线程我们叫做主线程或者叫做UI线 ...

  8. C#学习笔记之线程 - 通知Signal

    通知事件等待句柄 Signal With EventWaitHandle 事件等待句柄常用于通知.当一个线程等待直到接收到另外一个线程发出的信号.事件等待句柄是最简单的信号结构,它与C#事件无关.有三 ...

  9. Java多线程中线程间的通信

    一.使用while方式来实现线程之间的通信 package com.ietree.multithread.sync; import java.util.ArrayList; import java.u ...

随机推荐

  1. Spring Boot 整合 Redis 和 JavaMailSender 实现邮箱注册功能

    Spring Boot 整合 Redis 和 JavaMailSender 实现邮箱注册功能 开篇 现在的网站基本都有邮件注册功能,毕竟可以通过邮件定期的给用户发送一些 垃圾邮件 精选推荐

  2. CVE-2019-3396:Confluence未授权模板注入_代码执行

    title: Confluence未授权模板注入/代码执行(CVE-2019-3396) tags: [poc,cve] 简介 Confluence是一个专业的企业知识管理与协同软件,也可以用于构建企 ...

  3. 树莓派4B踩坑指南 - (13)用samba建立家庭局域网共享中心

    树莓派在家中至少三个作用:家庭资源共享中心.无线打印服务器.下载服务器. 家庭资源共享中心用samba实现家庭局域网共享,树莓派4B的话可以接2个3.0的移动硬盘. 实测速度不快,Win读2Mb/s写 ...

  4. latex中文支持ubuntu

    latex安装: sudo apt install texlive-full 中文字体安装: sudo apt-get install latex-cjk-all      字体包中包含bsmi,bk ...

  5. PyTorch专栏开篇

    目前研究人员正在使用的深度学习框架不尽相同,有 TensorFlow .PyTorch.Keras等.这些深度学习框架被应用于计算机视觉.语音识别.自然语言处理与生物信息学等领域,并获取了极好的效果. ...

  6. 前端学习(3)-CSS

    一 CSS CSS是Cascading Style Sheets的缩写,层叠样式表,用来控制网页数据的显示,可以使网页的显示与数据内容分离. 二 引入方式 (1)行内式:在标记的style属性中设置C ...

  7. Conda安装包错误-CondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://conda.anaconda.org/r/win-64/repodata.json> Elapsed:

    可能是防火墙问题:conda config --set ssl_verify false 安装 openssl . 换源: cmd输入conda config --add channels r 进入C ...

  8. SpringMVC里 form提交到controller404 解决方法

    把 <form action="/logon"> 改为 <form action="${pageContext.request.contextPath} ...

  9. 空间复杂度(Space Complexity)

    空间复杂度(Space Complexity) 算法得存储量包括: 1.程序本身所占空间. 2.输入数据所占空间. 3.辅助变量所占空间. 输入数据所占空间只取决于问题本身,和算法无关,则只需分析除输 ...

  10. 【故障公告】部署在 k8s 上的博客后台昨天与今天在访问高峰多次出现 502

    非常抱歉,从昨天上午开始,部署在 k8s 集群上的博客后台(基于 .NET Core 3.1 + Angular 8.2 实现)出现奇怪问题,一到访问高峰就多次出现 502 ,有时能自动恢复,有时需要 ...