ManualResetEvent是C#中System.Threading命名空间下的一个同步原语,属于WaitHandle的子类,主要用于控制多个线程的执行流程,让一个或多个线程等待某个条件的触发后再继续执行。

ManualResetEvent核心概念
ManualResetEvent内部维护一个布尔类型的状态值,这个状态值只有两种:终止状态和非终止状态。当处于终止状态时,所有调用WaitOne方法的线程都会立即返回,不会阻塞;当处于非终止状态时,调用WaitOne方法的线程会被阻塞,直到状态被设置为终止状态。
和AutoResetEvent最大的不同是,ManualResetEvent的状态需要手动重置,当状态被设置为终止后,会一直保持该状态,直到显式调用Reset方法将其重置为非终止状态,而AutoResetEvent在释放一个等待线程后会自动重置状态。
常用方法说明
- 构造函数:ManualResetEvent(bool initialState),参数initialState表示初始状态,true为终止状态,false为非终止状态。
- Set方法:将ManualResetEvent的状态设置为终止状态,唤醒所有正在等待的线程。
- Reset方法:将ManualResetEvent的状态设置为非终止状态,后续调用WaitOne的线程会被阻塞。
- WaitOne方法:阻塞当前线程,直到ManualResetEvent的状态变为终止状态,有多个重载可以设置等待超时时间。
基础使用示例
下面是一个简单的示例,主线程启动工作线程后,等待工作线程完成初始化,工作线程初始化完成后通过ManualResetEvent通知主线程继续执行。
using System;
using System.Threading;
namespace ManualResetEventDemo
{
class Program
{
// 初始状态为非终止状态,工作线程需要先完成初始化
static ManualResetEvent manualResetEvent = new ManualResetEvent(false);
static void Main(string[] args)
{
Console.WriteLine("主线程启动,开始启动工作线程");
Thread workThread = new Thread(WorkMethod);
workThread.Start();
Console.WriteLine("主线程等待工作线程初始化完成");
// 阻塞主线程,等待工作线程发出信号
manualResetEvent.WaitOne();
Console.WriteLine("主线程收到信号,继续执行后续逻辑");
// 等待工作线程执行完成
workThread.Join();
Console.WriteLine("程序执行结束");
}
static void WorkMethod()
{
Console.WriteLine("工作线程开始执行初始化操作");
// 模拟初始化耗时操作
Thread.Sleep(2000);
Console.WriteLine("工作线程初始化完成,发出信号通知主线程");
// 将状态设置为终止状态,唤醒等待的主线程
manualResetEvent.Set();
// 模拟工作线程后续执行逻辑
Thread.Sleep(1000);
Console.WriteLine("工作线程执行完成");
}
}
}
多等待线程场景示例
ManualResetEvent可以唤醒多个等待的线程,下面的示例启动三个工作线程,都等待同一个ManualResetEvent的信号,当信号触发时,三个线程会同时被唤醒。
using System;
using System.Threading;
namespace ManualResetEventMultiDemo
{
class Program
{
// 初始为非终止状态
static ManualResetEvent manualResetEvent = new ManualResetEvent(false);
static void Main(string[] args)
{
Console.WriteLine("启动三个工作线程,等待信号触发");
for (int i = 1; i <= 3; i++)
{
int threadId = i;
Thread thread = new Thread(() =>
{
Console.WriteLine($"工作线程{threadId}开始等待信号");
manualResetEvent.WaitOne();
Console.WriteLine($"工作线程{threadId}收到信号,开始执行任务");
});
thread.Start();
}
// 模拟主线程准备资源
Thread.Sleep(3000);
Console.WriteLine("主线程准备完成,触发信号唤醒所有工作线程");
manualResetEvent.Set();
Thread.Sleep(1000);
Console.WriteLine("程序执行结束");
}
}
}
状态手动重置示例
由于ManualResetEvent的状态需要手动重置,下面的示例展示如何重复使用同一个ManualResetEvent控制多轮线程等待。
using System;
using System.Threading;
namespace ManualResetEventResetDemo
{
class Program
{
static ManualResetEvent manualResetEvent = new ManualResetEvent(false);
static void Main(string[] args)
{
Thread workThread = new Thread(() =>
{
for (int i = 1; i <= 3; i++)
{
Console.WriteLine($"工作线程等待第{i}轮信号");
manualResetEvent.WaitOne();
Console.WriteLine($"工作线程收到第{i}轮信号,执行第{i}轮任务");
// 执行完任务后,重置状态,等待下一轮信号
manualResetEvent.Reset();
}
});
workThread.Start();
// 触发三轮信号
for (int i = 1; i <= 3; i++)
{
Thread.Sleep(2000);
Console.WriteLine($"主线程触发第{i}轮信号");
manualResetEvent.Set();
}
workThread.Join();
Console.WriteLine("程序执行结束");
}
}
}
使用注意事项
- ManualResetEvent使用完成后需要实现IDisposable接口释放资源,建议使用using语句包裹,避免资源泄漏。
- 如果初始状态设置为true,那么第一个调用WaitOne的线程不会被阻塞,会直接通过。
- 调用Set方法后,状态会一直保持终止,直到调用Reset,所以如果有需要重复等待的场景,一定要记得调用Reset方法。
- WaitOne方法可以设置超时时间,如果超时后状态仍未变为终止,方法会返回false,实际使用中可以根据返回值判断等待是否成功。
ManualResetEvent和AutoResetEvent对比
| 对比项 | ManualResetEvent | AutoResetEvent |
|---|---|---|
| 状态重置方式 | 手动调用Reset方法重置 | 释放一个等待线程后自动重置 |
| 唤醒线程数量 | 可以唤醒所有等待的线程 | 每次只能唤醒一个等待的线程 |
| 适用场景 | 需要通知多个线程同时开始执行,或者需要重复控制多轮等待的场景 | 需要逐个唤醒等待线程,控制线程依次执行的场景 |
实际应用场景
ManualResetEvent常见的应用场景包括:多个工作线程等待主线程的资源初始化完成后同时开始工作、控制线程池中的线程分批执行任务、实现线程的暂停和恢复功能等。在需要多个线程同步等待同一个条件的场景中,ManualResetEvent是非常合适的选择。
ManualResetEventC#线程同步WaitHandle修改时间:2026-07-12 18:00:27