我们惊讶地发现,等待ManualResetEvent
的线程即使在关闭时也会继续等待事件。我们原本期望调用Close()
会隐含地发出等待线程的信号。
我们跟踪这个问题是因为我们的一些Windows服务没有像我们想的那样快速关闭。我们首先更改了关闭Dispose
引用ManualResetEvent
的所有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
来运行此代码时,我们得到了这个..
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
答案 0 :(得分:2)
Close
是一种处理对象的方法(Close
和Dispose
对此类产生相同的行为)。它不会影响手柄的状态。假设在所有情况下,用户都希望线程等待我关闭继续的句柄似乎不合理。事实上,句柄正在使用的事实应该表明你不应该首先调用Close
。
这不是“为什么不应该Set
被隐含地调用?”,这是一个概念性问题:如果你打电话给Close
,你应该不再关心对象。使用Set
和Reset
来控制线程之间的执行流程;不要在任何包含Close
的对象上调用Dispose
(或WaitHandle
),直到它们不再使用为止。
答案 1 :(得分:1)
这些同步事件基于Win32等待句柄,Close()
方法仅释放它们(如Dispose()
)而不发出信号,等待线程一直在等待。