当前位置: 首页 > news >正文

C# ManualResetEvent 类 使用详解

总目录


前言

ManualResetEvent 是 C# 中用于线程同步的核心类之一,位于 System.Threading 命名空间下。它的核心功能是通过信号机制控制线程的执行顺序,允许一个或多个线程等待某个信号后再继续运行。与 AutoResetEvent 不同,ManualResetEvent 在被触发后会保持信号状态,直到显式地调用 Reset() 方法将其重置为非信号状态。这种特性使得它适用于需要广播多个线程的场景。


一、核心概念

  • 作用:通过信号机制控制线程的执行,允许一个或多个线程等待某个事件完成。
  • 信号状态
    • 有信号(Signaled):所有调用 WaitOne() 的线程不会被阻塞。
    • 无信号(Non-signaled):所有调用 WaitOne() 的线程会被阻塞,直到调用 Set()
  • 手动重置
    • 调用 Set() 后,事件保持有信号状态,需显式调用 Reset() 才能恢复无信号状态。
    • 意味着可以释放多个等待的线程。

本文中所描述的 有信号状态、终止状态、触发状态 意义相同,都是同一种状态的不同名称

二、基本用法

1. 构造函数

var manualEvent = new ManualResetEvent(initialState: false); // 初始无信号
  • initialState:初始化是否为有信号状态(true 表示有信号/或称 已触发,则线程一开始是无需等待信号的)。

2. 关键方法

方法作用
Set()将事件设为有信号状态,释放所有等待线程。
Reset()将事件设为无信号状态,后续的 WaitOne() 会阻塞。
WaitOne()阻塞当前线程,直到事件变为有信号状态。可以指定超时时间
Dispose()释放资源(继承自 WaitHandle)。

三、 示例

示例 1:单线程等待事件

using System.Threading;class Program
{static ManualResetEvent manualEvent = new ManualResetEvent(false);static void Main(){Thread worker = new Thread(DoWork);worker.Start();// 主线程触发信号Thread.Sleep(2000);Console.WriteLine("主线程发送信号");manualEvent.Set(); // 释放工作线程}static void DoWork(){Console.WriteLine("工作线程等待信号...");manualEvent.WaitOne(); // 阻塞直到信号触发Console.WriteLine("工作线程继续执行");}
}

输出

工作线程等待信号...
主线程发送信号
工作线程继续执行

示例 2:广播多个线程

static ManualResetEvent manualEvent = new ManualResetEvent(false);static void Main()
{// 启动3个等待线程for (int i = 0; i < 3; i++){new Thread(() => {manualEvent.WaitOne();Console.WriteLine($"线程 {Thread.CurrentThread.ManagedThreadId} 被唤醒");}).Start();}// 主线程触发广播Thread.Sleep(2000);manualEvent.Set(); // 所有等待线程同时释放manualEvent.Reset(); // 重置为无信号,后续新线程需要重新等待
}

输出

线程 4 被唤醒
线程 5 被唤醒
线程 6 被唤醒

四、与 AutoResetEvent 的区别

1. 区别

特性ManualResetEventAutoResetEvent
重置方式需显式调用 Reset()调用 Set() 后自动重置
唤醒线程数量唤醒所有等待线程每次 Set() 仅唤醒一个线程
适用场景广播通知(如初始化完成、批量任务开始)严格交替执行(如生产者-消费者模型)

2. 示例

  • AutoResetEvent 示例
class Program
{// 线程通知private static AutoResetEvent resetEvent = new AutoResetEvent(false);static void Main(string[] args){// 创建线程Thread worker = new Thread(DoWork);worker.Start();// 用于不断向另一个线程发送信号while (true){Console.ReadKey();resetEvent.Set();           // 按下任意键,将事件设为有信号状态,释放等待线程。}}public static void DoWork(){Console.WriteLine("① 等待中,请发出信号允许我运行");resetEvent.WaitOne();Console.WriteLine("② 等待中,请发出信号允许我运行");resetEvent.WaitOne();Console.WriteLine("③ 等待中,请发出信号允许我运行");// ...Console.WriteLine("线程结束");}
}

输出:按下任意键,按一下输出一下内容

① 等待中,请发出信号允许我运行
② 等待中,请发出信号允许我运行
③ 等待中,请发出信号允许我运行
线程结束
  • ManualResetEvent 示例
class Program
{private static ManualResetEvent resetEvent = new ManualResetEvent(false);static void Main(string[] args){// 创建线程Thread worker = new Thread(DoWork);worker.Start();// 用于不断向另一个线程发送信号while (true){Console.ReadKey();resetEvent.Set();          // 按下任意键,将事件设为有信号状态,释放【所有】等待线程。}}public static void DoWork(){Console.WriteLine("等待中,请发出信号允许我运行");resetEvent.WaitOne();// 后面的都无效,线程会直接跳过而无需等待Console.WriteLine("等待中,请发出信号允许我运行");resetEvent.WaitOne();Console.WriteLine("等待中,请发出信号允许我运行");resetEvent.WaitOne();Console.WriteLine("等待中,请发出信号允许我运行");resetEvent.WaitOne();Console.WriteLine("等待中,请发出信号允许我运行");resetEvent.WaitOne();Console.WriteLine("等待中,请发出信号允许我运行");resetEvent.WaitOne();Console.WriteLine("线程结束");}
}

输出:按下任意键,直接输出所有内容

等待中,请发出信号允许我运行
等待中,请发出信号允许我运行
等待中,请发出信号允许我运行
等待中,请发出信号允许我运行
等待中,请发出信号允许我运行
等待中,请发出信号允许我运行
线程结束
  • AutoResetEventWaitOne() 方法等待信号完毕后,会自动重置为无信号状态,相当于高速收费站自动闸门,一辆车过去后,机器自动关闸。

  • ManualResetEvent 相当于人工闸门,打开后需要人工关闭闸门,不然的话闸门会一直处于打开状态。

  • ManualResetEvent 主要用于更加灵活的线程信号传递场景。

五、高级用法

1. 超时等待

bool signaled = manualEvent.WaitOne(TimeSpan.FromSeconds(3));
if (!signaled)
{Console.WriteLine("等待超时");
}

六、注意事项

1. 资源释放

  • 使用 Dispose()using 块释放资源,避免句柄泄漏。
using (var manualEvent = new ManualResetEvent(false))
{// 使用 manualEvent
}

2. 避免死锁

  • 确保 Set()Reset() 的调用逻辑合理,避免线程永久阻塞。
  • 示例:忘记调用 Set()Reset()

3. 线程安全

  • 多线程环境下,确保对 Set()Reset() 的调用是线程安全的。

七、替代方案

  • ManualResetEventSlim:轻量级版本,性能更高(适合短期等待)。
  • Semaphore/SemaphoreSlim:控制并发线程数量。
  • TaskCompletionSource:基于任务的异步模式(TAP)。

八、常见问题

1. 问题:忘记调用 Reset()

  • 现象Set() 后事件保持有信号状态,后续所有 WaitOne() 直接通过。
  • 解决:在需要重新阻塞线程前调用 Reset()

2. 问题:多次调用 Set()

  • 现象:无影响,事件已处于有信号状态时,Set() 不会改变状态。

结语

回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。


参考资料:
ManualResetEvent的使用
C# ManualResetEvent 类的用法
C#学习(二十八)——ManualResetEvent的理解和使用
手动线程通知 ManualResetEvent

相关文章:

C# ManualResetEvent 类 使用详解

总目录 前言 ManualResetEvent 是 C# 中用于线程同步的核心类之一&#xff0c;位于 System.Threading 命名空间下。它的核心功能是通过信号机制控制线程的执行顺序&#xff0c;允许一个或多个线程等待某个信号后再继续运行。与 AutoResetEvent 不同&#xff0c;ManualResetEve…...

动态规划——路径问题②

文章目录 931. 下降路径最小和算法原理代码实现 64. 最小路径和算法原理代码实现 174. 地下城游戏算法原理代码实现 931. 下降路径最小和 题目链接&#xff1a;931. 下降路径最小和 算法原理 状态表示&#xff1a; 经验题目要求&#xff1a;dp[i][j]表示到达[i,j]位置时&…...

ChatGPT macOS 桌面应用让你的编程体验更上一层楼

高效开发必备&#xff1a;ChatGPT macOS 桌面应用亮点盘点 ©作者|Ninja Geek 来源|神州问学 通过 macOS 版 ChatGPT 应用&#xff0c;已经能够更好的和你的生产力工具无缝配合工作。 大概在三四周之前&#xff0c;Anthropic 在 Claude 上推出了一项名为 Computer Use 的功…...

Java持久化之--Spring Data JPA

1、简介 Java持久化技术是Java开发中比较重要的部分&#xff0c;主要用于将对象数据持久化到数据库&#xff0c;或者从数据库中查询数据&#xff0c;简化数据库的CRUD操作。 2、JPA简介 JPA&#xff08;Java Persistence API&#xff09;是Java实现ORM&#xff08;Object Re…...

excel里的函数技巧(持续更新中)

行转列 在 Excel 中&#xff0c;行转列&#xff08;将一行数据转换为一列&#xff0c;或者将一列数据转换为一行&#xff09;是一项常见的操作。你可以使用 转置 功能轻松实现这一操作。 TRANSPOSE(数组)...

基于python sanic框架,使用Nacos进行微服务管理

微服务软件系统构建方式,已经很普及了,通过开源的sanic进行微服务管理,便捷,技术也比较成熟,而在项目实际应用过程中,微服务类型不仅有java的,还有nodejs、python等,尤其是结合算法模型构建的python接口,需要在Nacos进行注册管理。本文内容耗时2天踏坑,亲测一切ok。 …...

Day84:数据可视化

数据可视化是数据分析的重要组成部分,它能直观地展现数据规律,使复杂数据变得易懂。Python 提供了多个数据可视化库,其中最常用的是 Matplotlib 和 Seaborn。今天,我们将学习如何使用这些工具绘制折线图、柱状图、散点图等。 1. 安装和导入库 如果你的 Python 没有安装 Ma…...

fetch() 与 XMLHttpRequest 的差异

fetch() 与 XMLHttpRequest 的差异 fetch() 的功能与 XMLHttpRequest 基本相同&#xff0c;都是向服务器发出 HTTP 请求&#xff0c;但有三个主要的差异。 &#xff08;1&#xff09;fetch()使用 Promise&#xff0c;不使用回调函数&#xff0c;因此大大简化了写法&#xff0…...

TDengine 产品由哪些组件构成

目 录 背景产品生态taosdtaosctaosAdaptertaosKeepertaosExplorertaosXtaosX Agent应用程序或第三方工具 背景 了解一个产品&#xff0c;最好从了解产品包括哪些内容开始&#xff0c;我这里整理了一份儿 TDegnine 产品包括有哪些组件&#xff0c;每个组件作用是什么的说明&a…...

.NET Web-静态文件访问目录浏览

一、Web根目录访问 创建wwwroot文件夹app.UseStaticFiles(); // 启⽤静态⽂件中间件url/路径 进行访问 二、Web根目录之外的文件 app.UseStaticFiles(new StaticFileOptions {FileProvider new PhysicalFileProvider(Path.Combine(builder.Environment.ContentRootPath,&qu…...

SQL数据清理:去除字段值中的多余符号(Demo例子)

目录 前言1. 基础2. 进阶 前言 Excel中有大量不合法的符号&#xff0c;导入到系统之后&#xff0c;数据库有很多脏数据&#xff0c;对此下述展开sql的清洗教程 在数据库的文本字段中&#xff0c;可能会存在多余的逗号或符号&#xff0c;如,销售,, 或 二手车,销售,,这种情况 希…...

.NET版Word处理控件Aspose.Words教程:使用 C# 删除 Word 中的空白页

Word 文档中的空白页会使其看起来不专业并扰乱流程。用户会遇到需要删除 Word 中的空白页的情况&#xff0c;但手动删除它们需要时间和精力。在这篇博文中&#xff0c;我们将探讨如何使用 C# 删除 Word 中的空白页。 本文涵盖以下主题&#xff1a; C# 库用于删除 Word 中的空…...

【工业场景】用YOLOv8实现火灾识别

火灾识别任务是工业领域急需关注的重点安全事项,其应用场景和背景意义主要体现在以下几个方面: 应用场景:工业场所:在工厂、仓库等工业场所中,火灾是造成重大财产损失和人员伤亡的主要原因之一。利用火灾识别技术可以及时发现火灾迹象,采取相应的应急措施,保障人员安全和…...

Flask Web开发的重要概念和示例

一口气列举Flask Web应用的所有概念和示例 Flask Web 应用基本框架 路由(Routing) 模版(Template) request 对象 JSON 数据处理 redirect 示例 文件上传示例 文件下载示例 Session 示例 Cookie操作 Flask Web 应用基本框架 这是一个 最基础的 Flask Web 应用,…...

【Antv G2 5.x】饼图添加点击事件,获取当前坐标数据

// 监听 tooltip:show 事件this.chart.on(tooltip:show, (event) => {this.currentShowTooltipName = event.data.items[0].name})// 监听绘图区plot的点击事件this.chart.on(interval:click, ev => {this.$emit(chartClick, this.currentShowTooltipName);})// 监听绘图…...

深度学习-112-大语言模型LLM之langchain的聊天模型概述和基本概念介绍

文章目录 1 概念指南Conceptual guide1.1 概念Concepts1.2 词汇表Glossary2 聊天模型Chat models2.1 概述Overview2.2 功能Features2.3 集成Integrations2.4 接口Interface2.4.1 关键方法Key methods2.4.2 输入和输出Inputs and outputs2.4.3 标准参数Standard parameters2.5 工…...

Vue.js 实现树形结构管理系统的前端设计与实现

Vue.js 实现树形结构管理系统的前端设计与实现: 在现代前端开发中&#xff0c;树形结构是一种常见的数据展示方式&#xff0c;尤其适用于需要展示层级关系的场景&#xff0c;如目录、文件、分类等。本文将详细介绍如何使用 Vue.js 和 Element UI 组件库实现一个功能强大且易于…...

OSPF高级特性(3):安全特效

引言 OSPF的基础我们已经结束学习了&#xff0c;接下来我们继续学习OSPF的高级特性。为了方便大家阅读&#xff0c;我会将高级特性的几篇链接放在末尾&#xff0c;所有链接都是站内的&#xff0c;大家点击即可阅读&#xff1a; OSPF基础&#xff08;1&#xff09;&#xff1a;工…...

Unity Shader Graph 2D - Procedural程序化图形转动的环状六边形

前言 Hexagon又称六边形,在游戏中是十分常见的基础形状,本文将使用程序化的六边形来制作多个环状六边形叠加的转动动画效果,实践Unity Shader Graph中的常用节点功能。 创建一个Shader Graph文件命名为Hexagon,并创建对应的材质球M_Hexagon,在Shader Graph中创建一…...

鸿蒙HarmonyOS NEXT开发:横竖屏切换开发实践

文章目录 一、概述二、窗口旋转说明1、配置module.json5的orientation字段2、调用窗口的setPreferredOrientation方法 四、性能优化1、使用自定义组件冻结2、对图片使用autoResize3、排查一些耗时操作 四、常见场景示例1、视频类应用横竖屏开发2、游戏类应用横屏开发 五、其他常…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战&#xff1a;腾讯云IM群组成员管理&#xff08;增删改查&#xff09; 一、前言 在社交类App开发中&#xff0c;群组成员管理是核心功能之一。本文将基于UniApp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

论文笔记——相干体技术在裂缝预测中的应用研究

目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术&#xff1a;基于互相关的相干体技术&#xff08;Correlation&#xff09;第二代相干体技术&#xff1a;基于相似的相干体技术&#xff08;Semblance&#xff09;基于多道相似的相干体…...

NPOI Excel用OLE对象的形式插入文件附件以及插入图片

static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...

关于easyexcel动态下拉选问题处理

前些日子突然碰到一个问题&#xff0c;说是客户的导入文件模版想支持部分导入内容的下拉选&#xff0c;于是我就找了easyexcel官网寻找解决方案&#xff0c;并没有找到合适的方案&#xff0c;没办法只能自己动手并分享出来&#xff0c;针对Java生成Excel下拉菜单时因选项过多导…...

如何应对敏捷转型中的团队阻力

应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中&#xff0c;明确沟通敏捷转型目的尤为关键&#xff0c;团队成员只有清晰理解转型背后的原因和利益&#xff0c;才能降低对变化的…...

机器学习的数学基础:线性模型

线性模型 线性模型的基本形式为&#xff1a; f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法&#xff0c;得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...

shell脚本质数判断

shell脚本质数判断 shell输入一个正整数,判断是否为质数(素数&#xff09;shell求1-100内的质数shell求给定数组输出其中的质数 shell输入一个正整数,判断是否为质数(素数&#xff09; 思路&#xff1a; 1:1 2:1 2 3:1 2 3 4:1 2 3 4 5:1 2 3 4 5-------> 3:2 4:2 3 5:2 3…...