2015-04-14 53 views
4

Control.Invoke()調用PostMessage()然後等待,直到UI線程處理完消息。那麼爲什麼它不調用SendMessage()(默認情況下會一直等到UI線程處理完消息)。爲什麼Control.Invoke()調用PostMessage()而不是SendMessage()?

+0

我認爲答案與PostMessage和SendMessage在接收端具有非常不同的行爲有關何時和如何處理消息(通過消息隊列或直接調用窗口過程)。由此可見,在兩者上都使用PostMessage。如果有人對此有良好的評價,請回答。 – MicroVirus

+2

@Micro:跨線程SendMessage調用**從不**導致直接調用相應的窗口過程。雖然PostMessage和SendMessage在接收端的行爲不同,但不同之處在於,發送的跨線程消息由任何消息檢索函數自動分配,而發佈的消息通過調用'DispatchMessage'。 @paul:'SendMessage'不能返回,直到調用返回。如果UI線程阻塞,那麼也會阻塞調用線程。 'PostMessage'是防止死鎖的一種方法。 – IInspectable

+0

@IInspectable但是當調用Control.Invoke()時,調用線程會等待,所以如果UI線程阻塞,這也會阻塞調用線程。 – paul

回答

4

Control.Invoke()是危險方法,許多.NET程序員已經死鎖了它的程序。應該非常強烈地避免因此。簡單的日常操作就像關閉窗戶一樣危險。你會希望等到工作線程不能再調用,因爲當線程繼續運行但UI已經消失時沒有什麼好的事情發生。所以你用AutoResetEvent指示線程並等待它完成。

當線程在錯誤的時間調用Invoke()時,這樣的等待很可能導致程序死鎖。線程無法完成,因爲它停留在Invoke()調用中,UI線程無法爲其服務,因爲它處於等待狀態。一個「致命的擁抱」,兩個線程都不能取得進展,你的程序會掛起。很難調試,因爲它不可預測,並且不會經常發生,只有在線程完全同時調用Invoke時纔會出錯。

打破死鎖需要知道Invoke()調用正在進行,因此可以取消。當你使用SendMessage()時是不可知的。它阻塞的鎖隱藏在操作系統中。我最近發佈了an answer關於SendMessage的問題,您在那裏閱讀的所有內容也適用於此處。

因此,微軟並沒有這樣實施,他們使用PostMessage。他們在調用隊列中添加一個條目,調用PostMessage來喚醒UI線程,以便查看該隊列。特定於通過BeginInvoke調用,它們阻塞隊列條目中的ManualResetEvent,當UI線程完成對委託目標的調用時發出信號。

現在他們可以做一些事情來避免死鎖,當一個窗口關閉時,它會通過調用隊列來查看並取消任何具有該窗口作爲調用目標的東西。或換句話說,當您使用SendMessage並導致死鎖時,隱藏的鎖現在變得可見並且可以釋放以打破死鎖。

相關問題