如何用Monitor.Wait和Pulse实现WaitHandle.WaitAll?求可行示例
问题描述
我在阅读Joel Albahari的线程相关内容时,他提到:
在简单场景中,模拟适用于一组等待句柄的静态方法很容易。调用WaitAll的等效操作不过是一个包含所有替代等待句柄所用标志的阻塞条件:
lock (_locker) while (!_flag1 && !_flag2 && !_flag3...) Monitor.Wait (_locker);
但我并不理解如何像实现AutoResetEvent和ManualResetEvent(例如ManualResetEventSlim的实现方式)那样,用Monitor.Wait和Monitor.Pulse实现可正常工作的WaitHandle.WaitAll替代方案。请问有人能提供可行的示例实现吗?
实现示例
我们可以先基于Monitor实现自定义的自动/手动重置事件,再在此基础上实现WaitAll的逻辑。
1. 自定义重置事件类
先实现一个支持自动/手动重置的基础事件类,模仿ManualResetEventSlim和AutoResetEvent的核心行为:
public class CustomResetEvent { private readonly object _locker = new object(); private bool _isSignaled; private readonly bool _autoReset; private readonly List<object> _sharedWaiters = new List<object>(); public CustomResetEvent(bool initialState, bool autoReset) { _isSignaled = initialState; _autoReset = autoReset; } // 注册共享等待锁,用于WaitAll时的唤醒通知 internal void RegisterSharedWaiter(object sharedLocker) { lock (_locker) { if (!_sharedWaiters.Contains(sharedLocker)) _sharedWaiters.Add(sharedLocker); } } public void Set() { lock (_locker) { if (!_isSignaled) { _isSignaled = true; Monitor.PulseAll(_locker); // 唤醒当前事件的等待线程 // 同时唤醒所有注册的共享等待锁(用于WaitAll) foreach (var waiter in _sharedWaiters) { lock (waiter) Monitor.PulseAll(waiter); } } } } public void Reset() { lock (_locker) { _isSignaled = false; } } public bool Wait(TimeSpan timeout) { lock (_locker) { if (_isSignaled) { if (_autoReset) _isSignaled = false; return true; } return Monitor.Wait(_locker, timeout); } } public void Wait() => Wait(Timeout.InfiniteTimeSpan); internal bool IsSignaled { get { lock (_locker) return _isSignaled; } } internal bool AutoReset => _autoReset; }
2. 实现WaitAll替代方法
基于自定义重置事件,实现类似WaitHandle.WaitAll的功能:等待所有传入事件都被触发,自动重置事件触发后会自动恢复未信号状态。
public static class CustomWaitHandle { public static void WaitAll(params CustomResetEvent[] events) { if (events == null || events.Length == 0) throw new ArgumentException("至少需要传入一个事件"); var sharedLocker = new object(); // 给每个事件注册共享等待锁,确保事件触发时能唤醒WaitAll的等待线程 foreach (var evt in events) { evt.RegisterSharedWaiter(sharedLocker); } lock (sharedLocker) { bool allSignaled; do { allSignaled = true; // 检查所有事件是否都处于信号状态 foreach (var evt in events) { if (!evt.IsSignaled) { allSignaled = false; break; } } if (!allSignaled) { Monitor.Wait(sharedLocker); } else { // 自动重置事件触发后重置状态 foreach (var evt in events) { if (evt.AutoReset) evt.Reset(); } } } while (!allSignaled); } } }
3. 使用示例
class Program { static void Main() { var autoEvt1 = new CustomResetEvent(false, autoReset: true); var manualEvt = new CustomResetEvent(false, autoReset: false); var autoEvt2 = new CustomResetEvent(false, autoReset: true); // 启动线程分别触发事件 Task.Run(() => { Thread.Sleep(1000); autoEvt1.Set(); Console.WriteLine("自动事件1触发"); }); Task.Run(() => { Thread.Sleep(2000); manualEvt.Set(); Console.WriteLine("手动事件触发"); }); Task.Run(() => { Thread.Sleep(3000); autoEvt2.Set(); Console.WriteLine("自动事件2触发"); }); Console.WriteLine("等待所有事件触发..."); CustomWaitHandle.WaitAll(autoEvt1, manualEvt, autoEvt2); Console.WriteLine("所有事件已触发,继续执行"); // 测试自动重置事件的复用性 Task.Run(() => { Thread.Sleep(1000); autoEvt1.Set(); Console.WriteLine("自动事件1再次触发"); }); Task.Run(() => { Thread.Sleep(2000); autoEvt2.Set(); Console.WriteLine("自动事件2再次触发"); }); Console.WriteLine("再次等待所有事件触发..."); CustomWaitHandle.WaitAll(autoEvt1, manualEvt, autoEvt2); Console.WriteLine("所有事件再次触发,程序结束"); } }
核心逻辑说明
- 自定义
CustomResetEvent通过Monitor.Wait/PulseAll实现信号等待与唤醒,同时支持自动/手动重置:自动重置事件在被等待到后自动恢复未信号状态,手动重置事件保持信号状态直到调用Reset。 WaitAll通过共享锁同步状态检查,每次循环确认所有事件是否都触发;若未全部触发则等待,直到任意事件触发后重新检查。所有事件触发后,自动处理自动重置事件的状态重置。- 事件触发时会同时唤醒自身等待线程和
WaitAll的共享等待线程,确保状态变化能及时被感知。
内容的提问来源于stack exchange,提问作者Ivan Petrov
相关产品推荐
相关产品推荐

