即使调用Close(),为什么在ManualResetEvent上等待的线程会继续等待?

今天我们惊讶地发现等待一个ManualResetEvent线程继续等待事件,即使它已经关闭。 我们原本期望调用Close()会隐式发出等待线程的信号。

我们追踪这一点是因为我们的一些Windows服务没有像我们想的那样快速关闭。 我们正在更改所有关闭ManualResetEvent引用的Dispose实现,先调用Set

任何人都可以解释为什么Close不会隐式调用Set ? 你什么时候想要一个等待的线程继续等待?

这是我们的测试代码,用于演示我们的发现:

  private static readonly Stopwatch _timer = Stopwatch.StartNew(); public static void Test() { var sync = new ManualResetEvent(false); ThreadPool.QueueUserWorkItem(state => { Log("ThreadPool enter, waiting 250ms..."); sync.WaitOne(250); Log("ThreadPool exit"); }); Log("Main sleeping 100"); Thread.Sleep(100); Log("Main about to close"); // sync.Set(); // Is Set called implicitly? No... sync.Close(); Log("Main waiting for exit 500ms"); Thread.Sleep(500); } private static void Log(string text) { Console.WriteLine("{0:0} {1}", _timer.ElapsedMilliseconds, text); } 

当我们使用Set call注释运行此代码时,我们得到了这个..

 0 Main sleeping 100 0 ThreadPool enter, waiting 250ms... 103 Main about to close 103 Main waiting for exit 500ms 259 ThreadPool exit 

当我们明确调用Set我们得到这个..

 0 Main sleeping 100 0 ThreadPool enter, waiting 250ms... 98 Main about to close 98 ThreadPool exit 98 Main waiting for exit 500ms 

Close是一种处理对象的方法( CloseDispose对此类产生相同的行为)。 它不会影响手柄的状态。 假设在所有情况下,用户都希望线程等待我关闭继续的句柄似乎不合理。 事实上,句柄正在使用的事实应该表明你不应该首先调用Close

这不是“为什么不应该隐式调用Set ?”的问题,这是一个概念问题:如果你正在调用Close你就不应该再关心这个对象了 。 使用SetReset来控制线程间的执行流程; 不要在任何对象上调用Close (或Dispose ),包括WaitHandle ,直到它们不再使用。

这些同步事件基于Win32等待句柄,而Close()方法仅释放它们(如Dispose() )而不发出信号,等待线程继续等待。