为什么Control.Invoke()调用PostMessage()而不是SendMessage()?

时间:2015-04-14 11:49:16

标签: c# .net winapi

Control.Invoke()调用PostMessage(),然后等待UI线程完成处理消息。那么为什么它不会调用SendMessage()(默认情况下等待UI线程完成处理消息)。

1 个答案:

答案 0 :(得分:4)

Control.Invoke()是一个危险的方法,许多.NET程序员用它来阻塞程序。因此,应该非常强烈地避免它。简单的日常操作,如关闭窗户变得危险。您将要等到工作线程无法再调用,因为当线程继续运行但UI消失时,没有任何好处发生。所以你用AutoResetEvent表示线程,并等待它完成。

当线程在错误的时间调用Invoke()时,这样的等待很可能使程序死锁。线程无法完成,因为它停留在Invoke()调用中,UI线程无法为其提供服务,因为它卡在等待中。这是一个“致命的拥抱”,任何一个线程都无法取得进展,你的程序也会挂起。很难调试,因为它不可预测且不经常发生,只有当线程在完全相同的时间调用Invoke时才会出错。

打破死锁需要知道Invoke()调用正在进行中,因此可以取消它。使用SendMessage()时无法知道。它阻止的锁定隐藏在操作系统中。我最近发布了关于SendMessage问题的an answer,你在这里阅读的所有内容都适用于此。

因此,Microsoft没有以这种方式实现它,并且他们使用PostMessage。他们在调用队列中添加一个条目,调用PostMessage来唤醒UI线程,以便查看该队列。特别是Invoke over BeginInvoke,它们阻塞队列条目中的ManualResetEvent,当UI线程完成对委托目标的调用时发出信号。

现在,他们可以采取措施避免死锁,当窗口关闭时,它会查看调用队列并取消任何将该窗口作为调用目标的窗口。或者换句话说,当你使用SendMessage并且导致死锁时看不见的锁现在变得可见并且可以释放以打破僵局。