深入解析Invoke and BeginInvoke, 同步與非同步解析
阿新 • • 發佈:2019-02-15
Invoke或者BeginInvoke的使用中無一例外地使用了委託Delegate,至於委託的本質請參考我的另一隨筆:。
一、為什麼Control類提供了Invoke和BeginInvoke機制? 關於這個問題的最主要的原因已經是dotnet程式設計師眾所周知的,我在此費點筆墨再次記錄到自己的日誌,以便日後提醒一下自己。 1、windows程式訊息機制
Windows程式有個訊息佇列,窗體上的所有訊息是這個佇列裡面訊息的最主要來源。這裡的while迴圈使用了GetMessage()這個方法,這是個阻塞方法,也就是佇列為空時方法就會被阻塞,從而這個while迴圈停止運動,這避免了一個程式把cpu無緣無故地耗盡,讓其它程式難以得到響應。當然在某些需要cpu最大限度運動的程式裡面就可以使用另外的方法,例如某些3d遊戲或者及時戰略遊戲中,一般會使用PeekMessage()這個方法,它不會被windows阻塞,從而保證整個遊戲的流暢和比較高的幀速。 這個主執行緒維護著整個窗體以及上面的子控制元件。當它得到一個訊息,就會呼叫DispatchMessage方法派遣訊息,這會引起對窗體上的視窗過程的呼叫。視窗過程裡面當然是程式設計師提供的窗體資料更新程式碼和其它程式碼。 2、dotnet裡面的訊息迴圈
public static void Main(string[] args)
{
Form f = new Form();
Application.Run(f);
}
Dotnet窗體程式封裝了上述的while迴圈,這個迴圈就是通過Application.Run方法啟動的。
3、執行緒外操作GUI控制元件的問題
如果從另外一個執行緒操作windows窗體上的控制元件,就會和主執行緒產生競爭,造成不可預料的結果,甚至死鎖。因此windows GUI程式設計有一個規則,就是隻能通過建立控制元件的執行緒來操作控制元件的資料,否則就可能產生不可預料的結果。
因此,dotnet裡面,為了方便地解決這些問題, Control類實現了ISynchronizeInvoke介面,提供了Invoke和BeginInvoke方法來提供讓其它執行緒更新GUI介面控制元件的機制。
public interface ISynchronizeInvoke
{
[HostProtection(SecurityAction.LinkDemand, Synchronization=true, ExternalThreading=true)]
IAsyncResult BeginInvoke(Delegate method, object[] args);
object EndInvoke(IAsyncResult result);
object Invoke(Delegate method, object[] args);
bool InvokeRequired { get; }
}
}
如果從執行緒外操作windows窗體控制元件,那麼就需要使用Invoke或者BeginInvoke方法,通過一個委託把呼叫封送到控制元件所屬的執行緒上執行。
二、訊息機制---執行緒間和程序間通訊機制
1、window訊息傳送
Windows訊息機制是windows平臺上的執行緒或者程序間通訊機制之一。Windows訊息值其實就是定義的一個數據結構,最重要的是訊息的型別,它就是一個整數;然後就是訊息的引數。訊息的引數可以表示很多東西。
這裡需要糾正一個誤區,那就是Control類上的非同步呼叫BeginInvoke並沒有開闢新的執行緒完成委託任務,而是讓介面控制元件的所屬執行緒完成委託任務的。看來非同步操作就是開闢新執行緒的說法不一定準確。
一、為什麼Control類提供了Invoke和BeginInvoke機制? 關於這個問題的最主要的原因已經是dotnet程式設計師眾所周知的,我在此費點筆墨再次記錄到自己的日誌,以便日後提醒一下自己。 1、windows程式訊息機制
Windows GUI程式是基於訊息機制的,有個主執行緒維護著一個訊息泵。這個訊息泵讓windows程式生生不息。
Windows程式有個訊息佇列,窗體上的所有訊息是這個佇列裡面訊息的最主要來源。這裡的while迴圈使用了GetMessage()這個方法,這是個阻塞方法,也就是佇列為空時方法就會被阻塞,從而這個while迴圈停止運動,這避免了一個程式把cpu無緣無故地耗盡,讓其它程式難以得到響應。當然在某些需要cpu最大限度運動的程式裡面就可以使用另外的方法,例如某些3d遊戲或者及時戰略遊戲中,一般會使用PeekMessage()這個方法,它不會被windows阻塞,從而保證整個遊戲的流暢和比較高的幀速。 這個主執行緒維護著整個窗體以及上面的子控制元件。當它得到一個訊息,就會呼叫DispatchMessage方法派遣訊息,這會引起對窗體上的視窗過程的呼叫。視窗過程裡面當然是程式設計師提供的窗體資料更新程式碼和其它程式碼。 2、dotnet裡面的訊息迴圈
Windows提供了一些api用來向一個執行緒的訊息佇列傳送訊息。因此,一個執行緒可以向另一個執行緒的訊息佇列傳送訊息從而告訴對方做什麼,這樣就完成了執行緒間的通訊。有些api傳送訊息需要一個視窗控制代碼,這種函式可以把訊息傳送到指定視窗的主執行緒訊息佇列;而有些則可以直接通過執行緒控制代碼,把訊息傳送到該執行緒訊息佇列中。
這裡需要糾正一個誤區,那就是Control類上的非同步呼叫BeginInvoke並沒有開闢新的執行緒完成委託任務,而是讓介面控制元件的所屬執行緒完成委託任務的。看來非同步操作就是開闢新執行緒的說法不一定準確。