C# EventWaitHandle
EventWaitHandle 是 .NET 中用于线程同步的一个核心基类,它封装了 Windows 操作系统中的 “事件内核对象(Event Kernel Object)”,用于在多个线程之间传递信号、协调执行顺序。
AutoResetEvent 和 ManualResetEvent 都是 EventWaitHandle 的派生类。理解 EventWaitHandle 有助于你更灵活地使用或自定义事件同步机制。
🧱 一、什么是 EventWaitHandle?
EventWaitHandle表示一个“线程同步事件”,它有两种状态:
- 非信号状态(nonsignaled):调用
WaitOne()的线程会被阻塞。- 信号状态(signaled):调用
WaitOne()的线程可以立即继续执行。
你可以通过 Set() 将其置为信号状态,通过 Reset() 置为非信号状态。
🔁 二、EventWaitHandle 的两种模式
EventWaitHandle 的行为由构造函数中的 EventResetMode 枚举决定:
public enum EventResetMode
{AutoReset, // 自动重置模式 → 相当于 AutoResetEventManualReset // 手动重置模式 → 相当于 ManualResetEvent
}
✅ 创建方式:
// 等价于 new AutoResetEvent(false)
var auto = new EventWaitHandle(initialState: false, mode: EventResetMode.AutoReset
);// 等价于 new ManualResetEvent(false)
var manual = new EventWaitHandle(initialState: false, mode: EventResetMode.ManualReset
);
💡 所以:
AutoResetEvent≈new EventWaitHandle(false, EventResetMode.AutoReset)
ManualResetEvent≈new EventWaitHandle(false, EventResetMode.ManualReset)
🛠 三、核心方法与属性
| 方法/属性 | 说明 |
|---|---|
WaitOne() | 阻塞当前线程,直到事件变为信号状态 |
WaitOne(int timeout) | 带超时的等待(毫秒),超时返回 false |
Set() | 将事件设为信号状态 |
Reset() | 将事件设为非信号状态 |
Close() / Dispose() | 释放底层操作系统句柄(推荐 using 或显式释放) |
🔐 四、底层原理(Windows 内核)
EventWaitHandle封装了 Windows API 中的CreateEvent内核对象。- 它是一个 内核模式同步对象(Kernel-mode synchronization object),因此:
- 可跨进程共享(通过命名事件)
- 性能开销比用户模式同步(如
Monitor、SpinLock)大 - 但功能更强(支持超时、跨进程等)
🌐 五、高级用法:命名事件(跨进程同步)
EventWaitHandle 支持命名事件,可用于不同进程间的线程同步。
示例:确保程序单实例运行
static void Main()
{bool createdNew;using var mutex = new Mutex(true, "MyAppSingletonMutex", out createdNew);// 但也可以用命名 EventWaitHandle 实现类似逻辑using var eventHandle = new EventWaitHandle(false,EventResetMode.ManualReset,"Global\\MyAppReadyEvent", // 名称,"Global\" 表示全局命名空间out createdNew);if (createdNew){Console.WriteLine("我是第一个实例,正在初始化...");Thread.Sleep(3000);eventHandle.Set(); // 通知其他实例我已就绪}else{Console.WriteLine("已有实例运行,等待其就绪...");eventHandle.WaitOne(); // 等待第一个实例发出信号Console.WriteLine("主实例已就绪,继续执行...");}
}
⚠️ 注意:
- 名称在系统范围内必须唯一
- 在 Windows 上,普通用户默认只能访问
Local\命名空间;需要管理员权限才能使用Global\- Linux/macOS(.NET Core+)也支持命名事件,但实现依赖于平台(如 POSIX 命名信号量)
🔄 六、与 WaitHandle.WaitAny / WaitAll 配合使用
EventWaitHandle 继承自 WaitHandle,因此可以和 WaitHandle.WaitAny、WaitHandle.WaitAll 一起使用,实现多事件等待。
示例:等待多个任务中的任意一个完成
var event1 = new EventWaitHandle(false, EventResetMode.AutoReset);
var event2 = new EventWaitHandle(false, EventResetMode.AutoReset);
var events = new WaitHandle[] { event1, event2 };// 启动两个任务
Task.Run(() => {Thread.Sleep(1000);event1.Set();
});Task.Run(() => {Thread.Sleep(2000);event2.Set();
});// 等待任意一个事件触发
int index = WaitHandle.WaitAny(events);
Console.WriteLine($"事件 {index} 先完成!");
✅ 这在异步协调、超时控制、多条件触发等场景非常有用。
⚖️ 七、性能与适用场景建议
| 场景 | 推荐方案 |
|---|---|
| 简单线程唤醒(单次) | AutoResetEvent 或 EventWaitHandle(AutoReset) |
| 广播唤醒所有线程 | ManualResetEvent |
| 跨进程同步 | 命名的 EventWaitHandle |
| 高性能、低延迟(同进程) | 优先考虑 Monitor.Wait/Pulse、TaskCompletionSource、SemaphoreSlim |
| 需要等待多个事件 | WaitHandle.WaitAny / WaitAll + EventWaitHandle |
❗ 注意:由于
EventWaitHandle涉及内核切换,每秒数千次以上的高频同步应避免使用它。
🧪 八、完整示例:生产者-消费者(使用 EventWaitHandle)
class Program
{static EventWaitHandle _itemAdded = new EventWaitHandle(false, EventResetMode.AutoReset);static Queue<int> _queue = new Queue<int>();static readonly object _lock = new object();static void Main(){new Thread(Consumer).Start();new Thread(Producer).Start();Console.ReadKey();}static void Producer(){for (int i = 0; i < 5; i++){lock (_lock){_queue.Enqueue(i);Console.WriteLine($"生产: {i}");}_itemAdded.Set(); // 通知消费者Thread.Sleep(500);}}static void Consumer(){while (true){_itemAdded.WaitOne(); // 等待有新项lock (_lock){if (_queue.Count > 0){int item = _queue.Dequeue();Console.WriteLine($"消费: {item}");}}}}
}
✅ 九、总结
| 特性 | 说明 |
|---|---|
| 本质 | 封装 Windows 事件内核对象的 .NET 类 |
| 两种模式 | AutoReset(过一人自动关) vs ManualReset(需手动关) |
| 继承关系 | EventWaitHandle ← WaitHandle ← MarshalByRefObject ← object |
| 关键能力 | 线程阻塞/唤醒、超时控制、跨进程同步、多事件等待 |
| 替代方案 | 高频场景用 SemaphoreSlim、Task、Channels 等更现代的并发原语 |
💡 记住:
EventWaitHandle是底层而强大的同步工具,适合中低频、需要精确控制或跨进程的场景。
日常开发中,若无特殊需求,优先考虑更高层的抽象(如Task、async/await、BlockingCollection)。
