C# 为什么等待ManualResetEvent的线程即使在调用Close()时也会继续等待?
今天我们惊奇地发现,等待C# 为什么等待ManualResetEvent的线程即使在调用Close()时也会继续等待?,c#,.net,multithreading,dispose,C#,.net,Multithreading,Dispose,今天我们惊奇地发现,等待ManualResetEvent的线程即使在事件关闭时也会继续等待该事件。我们希望调用Close()会隐式地向等待的线程发出信号 我们发现这是我们的一些windows服务没有像我们希望的那样快速关闭的原因。我们正在将所有关闭ManualResetEvent引用的Dispose实现更改为先调用Set 谁能解释一下为什么Close没有隐式调用Set?您希望等待线程何时继续等待 下面是我们的测试代码,以演示我们的发现: private static readonly S
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
调用注释的代码时,我们得到了这个
0 Main sleeping 100
0 ThreadPool enter, waiting 250ms...
103 Main about to close
103 Main waiting for exit 500ms
259 ThreadPool exit
0 Main sleeping 100
0 ThreadPool enter, waiting 250ms...
98 Main about to close
98 ThreadPool exit
98 Main waiting for exit 500ms
当我们显式调用Set
时,我们得到了这个结果
0 Main sleeping 100
0 ThreadPool enter, waiting 250ms...
103 Main about to close
103 Main waiting for exit 500ms
259 ThreadPool exit
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
),除非它们不再使用。这些同步事件基于Win32等待句柄,Close()
方法只释放它们(如Dispose()
),而不发送信号,等待的线程一直在等待。再加上这一点,将导致难以跟踪竞争条件,即使在OP的示例中,如果sync.Close();在线程池开始执行sync.WaitOne()之前碰巧被调用;我在这整件事上遇到的问题是,WaitHandle被设计为支持线程之间的同步,所以我觉得应该隐式地处理Set on Close和event Wait after Close。不过不用担心,我们创建了一个自定义扩展类,它添加了我们想要的功能。@Sam:我很高兴你找到了解决方案,但问题是,你关于对象生命周期的理念与设计人员的理念不符:仅仅是你关心句柄的状态就意味着你,通过他们的设计,尚未准备好调用Close
。