当前位置: 首页 > 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、游戏类应用横屏开发 五、其他常…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命

在华东塑料包装行业面临限塑令深度调整的背景下&#xff0c;江苏艾立泰以一场跨国资源接力的创新实践&#xff0c;重新定义了绿色供应链的边界。 跨国回收网络&#xff1a;废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点&#xff0c;将海外废弃包装箱通过标准…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

【JavaSE】绘图与事件入门学习笔记

-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角&#xff0c;以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向&#xff0c;距离坐标原点x个像素;第二个是y坐标&#xff0c;表示当前位置为垂直方向&#xff0c;距离坐标原点y个像素。 坐标体系-像素 …...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...

SQL Server 触发器调用存储过程实现发送 HTTP 请求

文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...