即使调用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
是一种处理对象的方法( Close
和Dispose
对此类产生相同的行为)。 它不会影响手柄的状态。 假设在所有情况下,用户都希望线程等待我关闭继续的句柄似乎不合理。 事实上,句柄正在使用的事实应该表明你不应该首先调用Close
。
这不是“为什么不应该隐式调用Set
?”的问题,这是一个概念问题:如果你正在调用Close
, 你就不应该再关心这个对象了 。 使用Set
和Reset
来控制线程间的执行流程; 不要在任何对象上调用Close
(或Dispose
),包括WaitHandle
,直到它们不再使用。
这些同步事件基于Win32等待句柄,而Close()
方法仅释放它们(如Dispose()
)而不发出信号,等待线程继续等待。