类 EventWaitHandle 允许线程通过信号和等待信号相互通信。 事件等待句柄(亦简称为“事件”)是可以收到信号以释放一个或多个等待线程的等待句柄。 收到信号后,事件等待句柄便会进行手动或自动重置。 该 EventWaitHandle 类可以表示本地事件等待句柄(本地事件)或命名系统事件等待句柄(命名事件或系统事件,对所有进程可见)。
注释
事件等待句柄不是 .NET 事件。 并不涉及任何委托或事件处理程序。 “event”一词用于描述它们,因为它们传统上称为操作系统事件,并且因为发出等待句柄信号的行为指示给等待线程表明一个事件已发生。
本地和命名事件等待句柄都使用系统同步对象,这些对象受 SafeWaitHandle 包装器保护,以确保释放资源。 使用 Dispose 完对象后,可以使用该方法立即释放资源。
自动重置的事件等待句柄
通过指定EventResetMode.AutoReset来创建自动重置事件,然后再创建EventWaitHandle对象。 顾名思义,在释放单个等待线程后,此同步事件会在发出信号后自动重置。 通过调用Set 方法来触发该事件。
自动重置事件通常用于一次为单个线程提供对资源的独占访问权限。 线程通过调用 WaitOne 方法请求资源。 如果没有其他线程持有等待句柄,该方法将 true
返回,调用线程可以控制资源。
重要
与所有同步机制一样,必须确保在访问受保护的资源前,所有代码路径都在相应的等待句柄上等待。 线程同步是协作的。
如果向自动重置事件发出信号时没有线程正在等待,此信号会一直发出到有线程尝试在等待句柄上等待。 该事件释放线程并立即重置,阻止后续线程。
手动重置的事件等待句柄
通过在创建EventResetMode.ManualReset对象时指定EventWaitHandle来创建手动重置事件。 顾名思义,必须在发出信号后手动重置此同步事件。 在重置之前,通过调用其 Reset 方法,等待事件句柄的线程会立即继续运行,而不会被阻塞。
手动重置事件如同畜栏口一样。 如果事件未收到信号,在事件句柄上等待的线程受阻止,如同畜栏中的马一样。 当事件发出信号时,通过调用其 Set 方法,所有等待线程都可以继续。 调用其 Reset 方法之前,事件保持在信号状态。 这使得手动复位事件成为在等待一个线程完成任务时阻塞其他线程的理想方法。
就像马离开畜栏一样,释放的线程需要时间才能被操作系统调度并恢复执行。 Reset如果在所有线程恢复执行之前调用该方法,则剩余线程再次阻止。 哪些线程继续执行,哪些线程阻塞取决于随机因素,例如系统负载、等待调度程序的线程数等。 如果发出信号的线程在发出信号后结束,这是最常见的使用模式,则这不是问题。 如果希望触发事件的线程在所有等待线程恢复后开始新任务,就必须阻止它,直到所有等待线程都已恢复。 否则,将会出现争用条件,而且代码行为也会变得不可预测。
自动和手动事件通用的功能
通常情况下,一个或多个线程在 EventWaitHandle 上一直受阻止到未受阻止的线程调用 Set 方法,此方法释放等待线程之一(如果是自动重置事件)或全部线程(如果是手动重置事件)。 线程可以向 EventWaitHandle 发出信号,然后调用静态 WaitHandle.SignalAndWait 方法以原子操作的形式在其中受阻止。
EventWaitHandle 对象可以与静态 WaitHandle.WaitAll 和 WaitHandle.WaitAny 方法一起使用。 由于这EventWaitHandleMutex两个类都派生自WaitHandle,因此可以将这两个类用于这些方法。
命名事件
Windows 操作系统允许命名事件等待句柄。 命名事件的范围覆盖整个系统。 也就是说,创建命名事件后,它对所有进程中的所有线程可见。 因此,命名事件可用于同步进程的活动以及线程。
可以使用指定事件名称的构造函数之一创建表示 EventWaitHandle 命名系统事件的对象。
注释
由于命名事件是系统范围的,因此可以有多个 EventWaitHandle 对象表示同一命名事件。 每次调用构造函数或 OpenExisting 方法时,都会创建一个新 EventWaitHandle 对象。 重复指定同一名称会创建表示同一命名事件的多个对象。
建议谨慎使用命名事件。 由于命名事件的范围覆盖整个系统,因此同名的另一进程可能会意外阻止线程。 在同一台计算机上执行的恶意代码可以使用此代码作为拒绝服务攻击的基础。
使用访问控制安全性来保护 EventWaitHandle 表示命名事件的对象,最好是使用指定对象的 EventWaitHandleSecurity 构造函数。 还可以使用 SetAccessControl 此方法应用访问控制安全性,但这会在创建事件等待句柄的时间和受保护时间之间留下漏洞窗口。 使用访问控制安全性保护事件有助于防止恶意攻击,但无法解决意外名称冲突的问题。
注释
与类 EventWaitHandle 不同,派生类 AutoResetEvent 和 ManualResetEvent 只能表示本地等待句柄。 它们不能表示命名的系统事件。