ManualResetEvent 是 C# 中的一个线程同步工具,用于线程间的信号传递。它通常用于控制线程的执行顺序,或者在多线程环境下进行某些操作的协调。它是 System.Threading 命名空间中的一个类,继承自 EventWaitHandle 类。
什么是 ManualResetEvent?
ManualResetEvent 是一种手动重置的事件对象。线程可以通过调用 Set() 方法来“触发”事件,通知一个或多个等待线程继续执行。当事件被触发时,等待的线程会被唤醒继续执行,直到调用 Reset() 方法将事件重置为非信号状态。
如何使用 ManualResetEvent?
Set():将事件设为有信号的状态,所有等待的线程会被唤醒。Reset():将事件设为无信号的状态,等待线程会被挂起,直到事件被重新设置为有信号状态。WaitOne():等待事件变为有信号的状态。如果事件已经有信号,线程将继续执行。如果没有信号,线程会阻塞,直到事件有信号。
使用场景
- 线程同步:你可以让多个线程在某个特定时刻开始工作。
- 线程间的信号传递:用来控制一个线程是否开始执行或继续执行,通常用来模拟信号量或线程间的条件变量。
代码示例:
using System;
using System.Threading;
class Program
{
static ManualResetEvent manualResetEvent = new ManualResetEvent(false); // 初始状态为无信号(false)
static void Main()
{
// 启动多个线程
Thread t1 = new Thread(Worker);
Thread t2 = new Thread(Worker);
t1.Start();
t2.Start();
Console.WriteLine("Press Enter to signal threads to start...");
Console.ReadLine();
// 通过 Set() 触发事件,允许线程继续执行
manualResetEvent.Set();
Console.WriteLine("Threads have been signaled to start.");
// 等待线程完成
t1.Join();
t2.Join();
Console.WriteLine("All threads have completed.");
}
static void Worker()
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " is waiting for signal...");
manualResetEvent.WaitOne(); // 阻塞,直到事件被 Set()
// 模拟工作
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " has started working.");
Thread.Sleep(2000); // 模拟一些工作
Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " has finished working.");
}
}
代码解释:
- 初始化
ManualResetEvent:manualResetEvent被初始化为false,表示开始时事件没有信号,线程会被阻塞等待。 - 创建线程:
t1和t2是两个线程,它们都会调用Worker方法。 WaitOne():在每个线程中,我们使用manualResetEvent.WaitOne()来让线程阻塞,直到事件变为有信号的状态。Set():当按下Enter键后,manualResetEvent.Set()会将事件设为有信号的状态,唤醒所有等待的线程。- 线程工作:一旦线程被唤醒,它们将继续执行并模拟工作。
Join():通过调用t1.Join()和t2.Join()等待线程完成,确保主线程在所有子线程完成后退出。
重要方法:
ManualResetEvent(bool initialState):构造函数,指定事件的初始状态。如果为true,事件立即为有信号状态;如果为false,事件为无信号状态。Set():设置事件为有信号状态,唤醒所有正在等待的线程。Reset():将事件状态重置为无信号状态,阻止等待的线程继续执行,直到再次调用Set()。WaitOne():让调用线程等待,直到事件状态为有信号状态。如果事件已经有信号,线程继续执行;否则,线程会阻塞。
注意事项:
- 手动重置:
ManualResetEvent是手动重置的。即一旦事件被设置为有信号状态,它会保持该状态直到显式调用Reset()。这意味着它不会像AutoResetEvent一样在唤醒一个线程后自动重置为无信号状态。 - 多线程操作:
ManualResetEvent适用于多个线程需要同时等待一个事件的场景,所有等待线程都会被唤醒。 - 性能考虑:虽然
ManualResetEvent是一个有效的同步机制,但在某些高频繁的同步需求中,可能会影响性能。要根据实际需求选择适当的同步工具。
AutoResetEvent vs. ManualResetEvent:
- AutoResetEvent:在每次唤醒一个线程后,自动将事件重置为无信号状态。
- ManualResetEvent:必须显式调用
Reset()来将事件重置为无信号状态,适合多个线程都等待同一个事件的场景。
总结:
ManualResetEvent 是一个非常有用的同步工具,适用于多个线程等待一个事件的场景。通过 Set()、Reset()和 WaitOne(),你可以控制线程的执行顺序和协作。
发表回复