Control.Invoke()调用PostMessage()然后等待直到 UI 线程完成处理消息。那么为什么它不调用SendMessage()(默认情况下等待 UI 线程完成处理消息)。
Control.Invoke() 是一种危险的方法,许多 .NET 程序员已经用它死锁了他们的程序。因此,应该非常强烈地避免它。关闭窗户等简单的日常操作变得危险。您将要等到工作线程无法再调用,因为当线程继续运行但 UI 消失时,不会发生任何好事。所以你用 AutoResetEvent 给线程发信号并等待它完成。
当线程在错误的时间调用 Invoke() 时,这种等待很可能会使您的程序死锁。线程无法完成,因为它卡在 Invoke() 调用中,UI 线程无法为其提供服务,因为它卡在等待中。一个“致命的拥抱”,两个线程都无法取得进展,您的程序将挂起。很难调试,因为它是不可预测的,而且发生的频率也不够高,只有在线程完全在同一时间调用 Invoke 时才会出错。
打破僵局需要知道 Invoke() 调用正在进行中,以便可以取消它。当您使用 SendMessage() 时,这是不可知的。它阻塞的锁隐藏在操作系统中。我最近发布了有关 SendMessage 问题的答案,您在那里阅读的所有内容也适用于此处。
所以微软没有以这种方式实现它,他们使用了 PostMessage。他们向调用队列添加一个条目,调用 PostMessage 来唤醒 UI 线程,以便它查看该队列。并且特定于 Invoke over BeginInvoke,它们阻塞队列条目中的 ManualResetEvent,当 UI 线程完成对委托目标的调用时发出信号。
现在他们可以做一些事情来避免死锁,当窗口关闭时,它会查看调用队列并取消任何以该窗口作为调用目标的窗口。或者换句话说,在您使用 SendMessage 时不可见并导致死锁的锁现在变得可见并且可以释放以打破死锁。