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()
,你可以控制线程的执行顺序和协作。
发表回复