C# 的 ManualResetEvent(线程同步操作) 类详解

C# 的 ManualResetEvent 类详解

作用

ManualResetEvent 是用于线程同步操作的类,允许一个或多个线程等待特定信号,以协调多个线程的执行顺序。它通过事件通知机制实现,确保线程在收到信号前保持阻塞,直到其他线程显式发出信号。

核心功能
  • 阻塞线程:调用 WaitOne() 使线程进入等待状态。

  • 发送信号:调用 Set() 将事件设为终止状态,释放所有等待线程。

  • 重置信号:调用 Reset() 将事件恢复为非终止状态。


信号状态:
  • 终止状态:所有调用 WaitOne(),线程不会被阻塞,直到调用Reset()。
  • 非终止状态:所有调用 WaitOne(),线程会被阻塞,直到调用set()。

特点
  1. 手动重置:调用 Set() 后需手动调用 Reset() 才能将状态恢复为非终止状态。

  2. 多线程释放:一旦处于终止状态(Signaled),所有等待线程立即释放,直到手动重置。

  3. 线程安全:所有方法(SetResetWaitOne)都是线程安全的。

  4. 跨进程支持:可通过命名方式在进程间同步(构造函数传名称)。


应用场景
  1. 初始化同步:主线程等待子线程完成初始化后再继续。

  2. 资源就绪通知:多个工作线程等待某个共享资源(如数据加载完成)。

  3. 阶段化任务:分阶段任务中,后续阶段需等待前一阶段所有线程完成。

  4. 高并发控制:替代锁机制,允许多个线程同时访问资源(需配合 Reset())。


基础用法
  1. 初始化:创建实例,参数指定初始状态(true 为终止状态)。

    ManualResetEvent mre = new ManualResetEvent(false); // 初始为非终止
  2. 阻塞线程:调用 WaitOne()

    mre.WaitOne(); // 阻塞,直到事件变为终止状态
  3. 发送信号:调用 Set()

    mre.Set(); // 设置为终止状态,释放所有等待线程
  4. 重置信号:调用 Reset()

    mre.Reset(); // 恢复为非终止状态


代码实例

场景 1:主线程等待子线程完成

用途

主线程需要等待子线程完成某个任务后再继续执行。例如:主线程启动后台任务后需等待其初始化完成,再执行后续操作。

代码逻辑
class Example
{static ManualResetEvent mre = new ManualResetEvent(false);static void Main(){Console.WriteLine("主线程启动工作线程。");Thread worker = new Thread(DoWork);worker.Start();Console.WriteLine("主线程等待信号...");mre.WaitOne(); // 阻塞主线程,直到子线程调用 Set()Console.WriteLine("主线程恢复执行。");}static void DoWork(){Console.WriteLine("工作线程执行任务...");Thread.Sleep(2000); // 模拟耗时操作mre.Set(); // 发送信号,唤醒主线程}
}
执行流程
  1. 主线程创建 ManualResetEvent 并初始化为非终止状态 (false)。

  2. 主线程启动子线程 DoWork

  3. 主线程调用 mre.WaitOne() 进入阻塞状态。

  4. 子线程执行任务(模拟耗时操作),完成后调用 mre.Set(),将事件状态设为终止。

  5. 主线程从 WaitOne() 处解除阻塞,继续执行后续代码。

输出结果
主线程启动工作线程。
主线程等待信号...
工作线程执行任务...
(等待 2 秒后)
主线程恢复执行。


场景 2:多个线程等待同一事件

用途

多个工作线程需要等待某个公共条件(如资源初始化完成)满足后,才能同时开始工作。例如:多个线程需等待数据库连接池初始化完成后才可执行查询。

代码逻辑
using System;
using System.Threading;class Example
{static ManualResetEvent mre = new ManualResetEvent(false);static void Main(){// 启动 3 个工作线程for (int i = 0; i < 3; i++){new Thread(Worker).Start(i);}// 启动初始化线程new Thread(Initialize).Start();}static void Initialize(){Console.WriteLine("初始化开始...");Thread.Sleep(3000);Console.WriteLine("初始化完成!");mre.Set(); // 通知所有等待线程}static void Worker(object id){Console.WriteLine($"线程 {id} 等待初始化...");mre.WaitOne(); // 阻塞,直到初始化线程调用 Set()Console.WriteLine($"线程 {id} 开始工作。");}
}
执行流程
  1. 主线程启动 3 个工作线程和一个初始化线程。

  2. 每个工作线程调用 mre.WaitOne() 进入阻塞状态,等待初始化完成。

  3. 初始化线程执行耗时操作(如加载配置),完成后调用 mre.Set()

  4. 所有等待的工作线程同时被唤醒,开始执行后续任务。

输出结果
线程 0 等待初始化...
线程 1 等待初始化...
线程 2 等待初始化...
初始化开始...
(等待 3 秒后)
初始化完成!
线程 0 开始工作。
线程 1 开始工作。
线程 2 开始工作。


场景 3:重复使用 ManualResetEvent

用途

需要多次复用同一个 ManualResetEvent 实例,分阶段同步多个任务。例如:分批次处理数据,每批任务完成后触发下一批任务。

代码逻辑
using System;
using System.Threading;class Example
{static ManualResetEvent mre = new ManualResetEvent(false);static void Main(){// 首次使用new Thread(() => Task("任务1")).Start();mre.WaitOne();mre.Reset(); // 重置为非终止状态// 第二次使用new Thread(() => Task("任务2")).Start();mre.WaitOne();}static void Task(string name){Console.WriteLine($"{name} 进行中...");Thread.Sleep(1000);mre.Set();}
}
执行流程
  1. 主线程启动第一个子线程执行 任务1

  2. 主线程调用 mre.WaitOne() 阻塞,等待 任务1 完成。

  3. 子线程 任务1 完成后调用 mre.Set(),主线程恢复执行。

  4. 主线程调用 mre.Reset() 将事件重置为非终止状态。

  5. 主线程启动第二个子线程执行 任务2,再次调用 mre.WaitOne() 阻塞。

  6. 子线程 任务2 完成后调用 mre.Set(),主线程恢复执行。

输出结果
任务1 进行中...
(等待 1 秒后)
任务2 进行中...
(等待 1 秒后)

三个场景关键区别总结

场景核心目的ManualResetEvent 操作要点
主线程等待子线程单向等待子线程完成子线程完成时调用 Set()
多线程等待同一事件广播式唤醒所有等待线程Set() 后无需立即 Reset()
重复使用事件对象分阶段同步任务每次使用后需调用 Reset() 重置状态

通过这三个场景,可以灵活掌握 ManualResetEvent 在不同线程同步需求中的使用技巧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/72189.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小白学习:提示工程(什么是prompt)

课程链接 https://www.bilibili.com/video/BV1PX9iYQEry/?spm_id_from333.337.search-card.all.click 一 什么是提示工程 【提示工程】也叫【指令工程】 prompt就是给大模型发的指令&#xff0c;如“给我讲个笑话” 懂得提示工程原理会带来什么优势 懂得原理 为什么有的指…

Docker Compose 之详解(Detailed Explanation of Docker Compose)

Docker Compose 之详解 当容器数量逐渐增多&#xff0c;你是否感到手忙脚乱&#xff1f;面对复杂的部署场景&#xff0c;是时候祭出神器Docker Compose了&#xff01;它能帮你优雅地管理多容器应用&#xff0c;一键启动、停止所有服务&#xff0c;不再为复杂的手动操作焦头烂额…

C语言 —— 此去经年梦浪荡魂音 - 深入理解指针(卷一)

目录 1. 内存和地址 2. 指针变量和地址 2.1 取地址操作符&#xff08;&&#xff09; 2.2 指针变量 2.3 解引用操作符 &#xff08;*&#xff09; 3. 指针的解引用 3.1 指针 - 整数 3.2 void* 指针 4. const修饰指针 4.1 const修饰变量 4.2 const修饰指针变量 5…

【AI】从头到脚详解如何创建部署Azure Web App的OpenAI项目

【AI】从头到脚详解如何创建部署Azure Web App的OpenAI项目 在Azure Web应用上,您可以使用Python的OpenAI包方便快捷地调用官方API,上传您的训练数据,并利用他们的算法进行处理。本教程提供了一个逐步指南,帮助您在Azure Web应用上部署您的OpenAI项目,涵盖了从资源设置到…

机器视觉工程师红外相机的选择:红外长波工业相机和短波红外工业相机玄机大总结

红外长波(LWIR)和短波(SWIR)工业相机在原理、应用场景和技术特点上有显著差异。以下是它们的对比分析: 1. 波长范围与成像原理 2. 技术特点 3. 典型应用场景 4. 优缺点对比 LWIR优势: 无需光照,适用于完全黑暗环境。 直接反映物体温度分布。 对烟雾、灰尘穿透能力强。…

uni-app学习笔记——自定义模板

一、流程 1.这是一个硬性的流程&#xff0c;只要按照如此程序化就可以实现 二、步骤 1.第一步 2.第二步 3.第三步 4.每一次新建页面&#xff0c;都如第二步一样&#xff1b;可以选择自定义的模版&#xff08;vue3Setup——这是我自己的模版&#xff09;&#xff0c;第二步的…

DeepSeek模型本地化部署方案及Python实现

DeepSeek实在是太火了&#xff0c;虽然经过扩容和调整&#xff0c;但反应依旧不稳定&#xff0c;甚至小圆圈转半天最后却提示“服务器繁忙&#xff0c;请稍后再试。” 故此&#xff0c;本文通过讲解在本地部署 DeepSeek并配合python代码实现&#xff0c;让你零成本搭建自己的AI…

Vue3计算属性深度解析:经典场景与Vue2对比

一、计算属性的核心价值 计算属性&#xff08;Computed Properties&#xff09;是Vue响应式系统的核心特性之一&#xff0c;它通过依赖追踪和缓存机制优雅地解决模板中复杂逻辑的问题。当我们需要基于现有响应式数据进行派生计算时&#xff0c;计算属性总能保持高效的性能表现…

python-leetcode-删除链表的倒数第 N 个结点

LCR 021. 删除链表的倒数第 N 个结点 - 力扣&#xff08;LeetCode&#xff09; 可以使用双指针方法来解决这个问题&#xff0c;这样可以在一次遍历内完成删除操作&#xff0c;从而达到 O(n) 的时间复杂度。以下是 Python 代码实现&#xff1a; 解题思路&#xff1a; 初始化快…

vue2的webpack(vue.config.js) 怎么使用请求转发 devServer.proxy

首先用 express 搭建后端服务器&#xff0c;注意使用中间件解析json格式的请求体&#xff0c;才会获取到 post 参数 app.use(express.json()); app.js const express require(express) const app express() app.use(express.json()); const port 3000app.post(/api/vue2, …

Linux:基本指令与内涵理解

1.文件操作指令 1.1 ls ls指令用于查看指定层级文件夹下的文件或文件夹 基本格式&#xff1a;ls (选项) (查看层级&#xff09; 其中选项处不写就默认是显示文件名&#xff0c;查看层级默认是当前层级 选项1&#xff1a; -l 作用&#xff1a;将查找文件的详细信息显示出来 我们…

SpaceSync智能排班:重构未来办公空间的神经中枢

文心智能体平台可免费使用DeepSeek 满血版啦&#xff0c;使用DeepSeek模型创建并提交智能体&#xff0c;即有机会瓜分万元奖金&#xff01;有这等好事还不快冲&#xff01; 文心智能体官网&#xff1a;文心智能体平台AgentBuilder | 想象即现实 本片文章为作者参加文心智能体平…

flutter dio库 源码赏析

1. factory函数 //调用factory构造方法后&#xff0c;实际返回的是Dio的子类 Dio dio Dio();abstract class Dio {factory Dio([BaseOptions? options]) > createDio(options); } 2. CancelToken 作用:取消操作 CancelToken cancelToken CancelToken();//监听取消 ca…

RGV调度算法

1、基于时间窗 https://wenku.baidu.com/view/470e9fd8b4360b4c2e3f5727a5e9856a57122693.html?_wkts_1741880736197&bdQuery%E7%8E%AF%E7%A9%BF%E8%B0%83%E5%BA%A6%E7%AE%97%E6%B3%95 2.2019年MathorCup高校数学建模挑战赛B题 2019-mathorcupB题-环形穿梭机调度模型&a…

基于CATIA VBA与Python的自动化音乐生成技术对比研究

在工程软件二次开发领域&#xff0c;CATIA 也可以许多另类的玩法。通过CATIA自带的VBA可以演奏歌曲&#xff0c;但实际效果往往差强人意。为了进一步优化实际演奏效果&#xff0c;本文以自动生成林宥嘉《说谎》钢琴前奏旋律为案例&#xff0c;探讨两种语言在多媒体控制领域的技…

最大数位置(信息学奥赛一本通-2038)

【题目描述】 输入n个整数,存放在数组a[1]至a[n]中&#xff0c;输出最大数所在位置(n≤1000)。 【输入】 第一行&#xff0c;数的个数n; 第二行&#xff0c;n个正整数&#xff0c;每个数在232−1之内。 【输出】 最大数所在位置。 【输入样例】 5 67 43 90 78 32 【输出样例】 …

【AIGC】OpenAI 集成 Langchain 操作实战使用详解

目录 一、前言 二、前置准备 2.1 安装 Langchain必须的依赖 2.1.1 python环境 2.1.2 langchain openai 环境 2.1.3 准备一个apikey 2.1.4 langchain 核心组件 三、Langchain 各组件使用 3.1 Chat models组件 3.1.1 Invocation 使用 3.1.1.1 结果解析 3.2 提示词模板…

【C#学习笔记04】深入掌握C语言格式化输出

引言 ​​printf()​​函数不仅可以将数据输出到控制台&#xff0c;还可以通过格式化字符串灵活地控制输出的格式。​​printf()​​​函数的使用规则&#xff0c;包括标志说明、字段宽度、转换精度、长度修饰、转换说明、转义字符和返回结果等内容。 1. ​​printf()​​函数…

python-leetcode-定长子串中元音的最大数目

1456. 定长子串中元音的最大数目 - 力扣&#xff08;LeetCode&#xff09; 可以使用 滑动窗口 方法来解决这个问题。步骤如下&#xff1a; 初始化&#xff1a;计算前 k 个字符中元音字母的个数&#xff0c;作为初始窗口的值。滑动窗口&#xff1a;遍历字符串&#xff0c;每次右…

蓝桥真题讲解

第一题 题目链接 0贪吃蛇长度 - 蓝桥云课 题目解析 题意&#xff1a;数#个数和个数再加上首尾 代码原理 略 代码编写 略 填空题技巧 眼看手数 当然并不是真的一个一个数&#xff0c;我们需要借助一些工具&#xff0c;不过各位小伙伴们放心&#xff0c;我们借助的工具…