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

C#中EventWaitHandle的使用小结

深入介绍EventWaitHandle

EventWaitHandle是 .NET 中一个用于线程同步的基类,位于System.Threading命名空间下。它提供了一种机制,用于一个或多个线程等待某个特定事件的发生,通常用于多线程同步和线程间的通信。EventWaitHandle类本身是一个抽象类,不能直接实例化,但它有两个常见的子类——AutoResetEventManualResetEvent,这两个子类广泛用于线程同步操作。

1.EventWaitHandle基本概念与功能

EventWaitHandle 是一种线程同步机制,它使得线程能够根据特定事件的状态(信号或非信号)来决定是否继续执行。通常,线程在执行过程中会检查事件的状态,若事件处于非信号状态,线程会被挂起直到事件状态变为信号状态。

EventWaitHandle 可以在两个主要的状态之间切换:

  • 信号状态:表示事件已经触发,等待的线程可以继续执行。
  • 非信号状态:表示事件未触发,等待的线程会被阻塞,直到事件变为信号状态。

常见操作方法

  • Set():将事件的状态设置为信号状态,允许所有等待的线程继续执行。
  • Reset():将事件的状态设置为非信号状态,阻止所有等待线程继续执行,直到事件状态再次被触发。
  • WaitOne():使当前线程等待,直到事件的状态变为信号状态。线程如果发现事件是非信号状态,它会被阻塞,直到事件变为信号状态。

2.EventWaitHandle的实现机制

EventWaitHandle 是由操作系统支持的同步对象,通常通过系统级别的信号机制来实现。其工作原理主要包括:

  • 信号和无信号状态:EventWaitHandle 管理一个信号和非信号的状态。线程通过 WaitOne() 等待事件变为信号状态,而 Set() 方法将事件的状态设置为信号状态,解除线程的阻塞。
  • 线程阻塞:当线程调用 WaitOne() 方法时,若事件处于无信号状态,线程会被阻塞,直到 Set() 方法被调用,事件状态变为信号状态,线程才能继续执行。

事件复位机制

  • 自动复位:对于 AutoResetEvent 类,事件会在释放一个等待线程后自动返回到无信号状态。适用于需要逐个释放线程的情况。
  • 手动复位:对于 ManualResetEvent 类,事件保持信号状态,直到手动调用 Reset()。这适用于需要多个线程同时释放的场景。

3.EventWaitHandle的常用子类

(1)AutoResetEvent

AutoResetEvent 是 EventWaitHandle 的一个子类。它的特点是,事件在信号状态下会在释放一个等待的线程后自动回到无信号状态。每次信号事件被触发时,只有一个线程会被唤醒并执行,然后事件会自动复位。

使用场景
  • 单线程等待:AutoResetEvent 适用于需要单个线程继续执行的场景。每次事件被设置为信号状态后,只有一个线程会被唤醒并继续执行。
  • 生产者-消费者模式:适用于多个消费者线程处理来自生产者线程的数据,确保每次只允许一个消费者处理数据。

示例代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

usingSystem;

usingSystem.Threading;

classProgram

{

staticAutoResetEvent autoResetEvent =newAutoResetEvent(false);

staticvoidMain()

{

// 启动多个线程

for(inti = 0; i < 3; i++)

{

intthreadId = i;

newThread(() =>

{

Console.WriteLine($"Thread {threadId} is waiting...");

autoResetEvent.WaitOne();// 等待信号

Console.WriteLine($"Thread {threadId} is proceeding after signal.");

}).Start();

}

// 主线程模拟工作并发出信号

Thread.Sleep(2000);

Console.WriteLine("Main thread is signaling...");

// 每次调用 Set() 唤醒一个线程

for(inti = 0; i < 3; i++)

{

autoResetEvent.Set();// 唤醒一个线程

}

}

}

解释
  • 主线程调用autoResetEvent.Set(),每次触发信号后,只有一个等待的线程会被唤醒。
  • 线程在等待信号时使用WaitOne()方法,如果事件未触发,它将阻塞,直到事件状态变为信号。

(2)ManualResetEvent

ManualResetEventEventWaitHandle的另一种子类。与AutoResetEvent不同,ManualResetEvent在调用Set()后保持信号状态,直到显式调用Reset()来恢复为无信号状态。这意味着,多个线程可以同时继续执行,直到事件被重置。

使用场景
  • 多线程同时启动:多个线程可以在信号状态下同时继续执行。适用于所有线程必须在某个时刻开始执行的场景。
  • 批处理操作:当多个线程的执行依赖于某个条件时,可以使用ManualResetEvent等待某个条件达成后再同时启动多个线程。
示例代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

usingSystem;

usingSystem.Threading;

classProgram

{

staticManualResetEvent manualResetEvent =newManualResetEvent(false);

staticvoidMain()

{

// 启动多个工作线程

for(inti = 0; i < 3; i++)

{

intthreadId = i;

newThread(() =>

{

Console.WriteLine($"Thread {threadId} is waiting...");

manualResetEvent.WaitOne();// 等待信号

Console.WriteLine($"Thread {threadId} is proceeding after signal.");

}).Start();

}

// 主线程等待 2 秒,模拟一些工作

Thread.Sleep(2000);

Console.WriteLine("Main thread is signaling...");

// 设置信号状态,允许所有线程继续

manualResetEvent.Set();

}

}

解释
  • 在主线程调用Set()后,所有等待的线程都会继续执行,直到Reset()被调用。
  • AutoResetEvent不同,ManualResetEvent会保持信号状态,直到显式地调用Reset()来使其变为无信号状态。

4.EventWaitHandle和其子类的适用场景

特性AutoResetEventManualResetEvent
复位机制自动复位,释放一个等待线程后自动重置为无信号状态手动复位,信号状态保持,直到显式调用 Reset()
适用场景单线程等待,每次仅唤醒一个线程多线程等待,可以唤醒多个线程
线程释放只释放一个线程可以释放多个线程
线程阻塞线程阻塞直到事件变为信号状态线程阻塞直到事件变为信号状态,且多个线程可以并发执行

选择指南

  • 使用 AutoResetEvent:适用于逐个释放线程的场景,如生产者-消费者模型中的消费者线程,或者线程需要按顺序依次执行的情况。
  • 使用 ManualResetEvent:适用于一次性释放多个线程的场景,例如所有线程等待某个条件的达成,或者你希望多个线程同时执行某个任务。
http://www.jsqmd.com/news/876105/

相关文章:

  • Windows右键菜单终极管理指南:如何用ContextMenuManager打造高效工作流
  • Poppler-Windows在Windows平台上的3种高效部署方案:专业级PDF处理工具终极指南
  • 长沙家里黄金放着不增值?本地合扬首推 5 个变现方案 - 李宏哲1
  • 如何轻松提取和转换Wallpaper Engine资源文件?RePKG工具完全指南
  • 小红书数据采集实战指南:3大核心策略与完整API封装方案
  • 深入Linux内核:PTP硬件时间戳(HW Timestamping)是如何炼成的?
  • 2026年必看:论文遭导师怒批AI味太重?手把手教你降AI率,高效过审! - 降AI实验室
  • 终极GTA5线上小助手:免费开源工具让你的洛圣都冒险更高效
  • CANN-昇腾NPU-量化训练-QAT和PTQ怎么选
  • C#中TaskFactory实现线程任务
  • Ubuntu 20.04 上为 RTX 3060 编译 OpenCV 4.2.0 + CUDA 时,我踩过的那些坑(附完整解决方案)
  • LLM应用开发之模型微调技术详解
  • 3步轻松解密网易云音乐NCM文件:免费实现音乐跨平台播放
  • NHSE终极指南:动物森友会存档编辑器的5个核心应用场景
  • SketchUp STL插件终极指南:5分钟掌握3D打印模型转换的完整方案
  • GMERF与MERF:处理过离散计数数据的小域估计方法对比
  • JMeter接口测试工业化实践:从脚本编写到CI/CD全链路
  • 茉莉花插件终极指南:如何在3分钟内彻底解决Zotero中文文献管理难题
  • 接口测试三层防御体系:契约校验、逻辑穿透与系统压测
  • Godot 4.3本地AI编程助手:GDScript智能协作者实战指南
  • Edge和Chrome同时罢工?可能是这个Windows服务在搞鬼!附一键排查脚本
  • 3分钟掌握SketchUp STL插件:3D打印模型转换的完整解决方案
  • 终极猫抓浏览器扩展:5个简单步骤轻松捕获在线视频资源的完整指南
  • 高斯随机定时器原理与JMeter压测行为建模
  • JMeter+InfluxDB+Grafana压测监控实时可视化实战
  • TranslucentTB:Windows任务栏透明美化终极指南,轻松打造个性化桌面
  • 第七史诗自动化助手E7Helper:解放双手的游戏效率革命
  • E7Helper:第七史诗自动化助手终极指南,告别重复刷图烦恼
  • 解锁音乐自由:qmcdump如何让被加密的音乐重获新生?
  • 机器学习势函数与连续介质模型在二维材料原子重构中的对比研究