为啥即使调用 Close(),等待 ManualResetEvent 的线程也会继续等待?
Posted
技术标签:
【中文标题】为啥即使调用 Close(),等待 ManualResetEvent 的线程也会继续等待?【英文标题】:Why do threads waiting on a ManualResetEvent continue waiting even when Close() is called?为什么即使调用 Close(),等待 ManualResetEvent 的线程也会继续等待? 【发布时间】:2010-04-06 16:03:04 【问题描述】:今天我们惊讶地发现,等待 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
当我们显式调用Set
时,我们会得到这个..
0 Main sleeping 100
0 ThreadPool enter, waiting 250ms...
98 Main about to close
98 ThreadPool exit
98 Main waiting for exit 500ms
【问题讨论】:
【参考方案1】:Close
是一种处理对象的方法(Close
和 Dispose
在此类上产生相同的行为)。它不影响手柄的状态。假设在所有情况下,用户都希望线程在我关闭的句柄上等待继续,这似乎是不合理的。事实上,句柄正在使用这一事实应该表明你不应该首先调用Close
。
这不是“为什么不应该隐式调用Set
?”的问题,这是一个概念问题:如果你调用Close
,你应该不再关心对象。使用Set
和Reset
控制线程间的执行流程;不要在任何对象上调用Close
(或Dispose
),包括WaitHandle
s,直到它们不再被使用。
【讨论】:
此外,它会导致难以跟踪竞争条件,即使在 OP 的示例中,如果 sync.Close(); 也会遇到麻烦。碰巧在线程池开始执行sync.WaitOne();之前被调用 我遇到的问题是 WaitHandle 旨在支持线程之间的同步,所以我觉得 Set on Close 和事件 Wait after Close 应该被隐式处理。不过不用担心,我们创建了一个自定义扩展类来添加我们想要的功能。 @Sam:我很高兴你找到了解决方案,但同样,问题是你关于对象生命周期的理念与设计者的理念不符:事实上,你 关心句柄处于什么状态意味着你,按照他们的设计,还没有准备好打电话给Close
。【参考方案2】:
这些同步事件基于 Win32 等待句柄,Close()
方法只释放它们(如Dispose()
)而不发信号,等待线程一直等待。
【讨论】:
以上是关于为啥即使调用 Close(),等待 ManualResetEvent 的线程也会继续等待?的主要内容,如果未能解决你的问题,请参考以下文章
即使在 connection.close() 调用之后,连接仍处于建立状态
为啥实现Autoclosable接口的类的close方法比Sql连接关闭先调用?
为啥 Oledb Connection.Close() 执行时间太长?