一、Delegate的Invoke、BeginInvoke
1、Delegate.Invoke (委托同步調用)
a、委托的Invoke方法,在當前線程中執行委托。
b、委托執行時阻塞當前線程,知道委托執行完畢,當前線程才繼續向下執行。
c、委托的Invoke方法,類似方法的常規調用。
2、Delegate.BeginInvoke (委托異步調用)
a、委托的BeginInvoke方法,在線程池分配的子線程中執行委托
b、委托執行時不會阻塞主線程(調用委托的BeginInvoke線程),主線程繼續向下執行。
c、委托執行時會阻塞子線程。
d、委托結束時,如果有返回值,子線程講返回值傳遞給主線程;如果有回調函數,子線程將繼續執行回調函數。
3、Demo
a、Delegate
1 private void btn_General_Click(object sender, EventArgs e) 2 { 3 txt_Message.Text = ""; 4 txt_Message.Text += "主線程:"+ Thread.CurrentThread.ManagedThreadId + "---開始工作\r\n"; 5 //委托方法,在調用委托的線程中執行,本例中就是主線程(UI線程)。 6 //執行一些耗時的操作,就會阻塞主線程(UI線程) 7 //委托的普通調用就等於方法的直接調用,del();等價於SomeWork(); 8 del(); 9 //SomeWork(); 10 txt_Message.Text += "\r\n主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始結束\r\n"; 11 }
b、Delegate.Invoke
1 private void btn_Main_Invoke_Click(object sender, EventArgs e) 2 { 3 txt_Message.Text = ""; 4 txt_Message.Text += "主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始工作\r\n"; 5 //委托的同步調用,其實就是等價於委托的普通調用。 6 del.Invoke(); 7 txt_Message.Text += "\r\n主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始結束\r\n"; 8 }
1 private void btn_Sub_Invoke_Click(object sender, EventArgs e) 2 { 3 txt_Message.Text = ""; 4 txt_Message.Text += "主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始工作\r\n"; 5 6 //開啟新的線程執行委托,主線程(UI線程)繼續向下執行 7 new Thread(() => { 8 txt_Message.Text += "\r\n----子線程:" + Thread.CurrentThread.ManagedThreadId + "---開始工作\r\n"; 9 //委托在調用線程中執行,並阻塞調用線程,知道委托方法執行結束。 10 del.Invoke(); 11 txt_Message.Text += "\r\n----子線程:" + Thread.CurrentThread.ManagedThreadId + "---開始結束\r\n"; 12 }).Start(); 13 14 txt_Message.Text += "\r\n主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始結束\r\n"; 15 }
c、Delegate.BeginInvoke
1 private void btn_Main_BeginInvoke_Click(object sender, EventArgs e) 2 { 3 txt_Message.Text = ""; 4 txt_Message.Text += "主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始工作\r\n"; 5 //委托異步調用 6 //1、委托方法,在線程池中分配的子線程中執行。 7 //2、主線程和子線程同時執行。 8 //3、子線程結束之后,如果有返回值得話,將返回值傳遞給主線程。如果有回調函數的話,繼續在子線程中執行回調函數。 9 10 //有異常,控件不能在子線程中訪問修改。 11 //避免這類異常有兩種方法 12 //1、手動關閉控件的跨線程安全檢查Control.CheckForIllegalCrossThreadCalls = false;(不建議使用) 13 //2、使用控件的Invoke方法。(推薦使用) 14 del.BeginInvoke(null,null); 15 txt_Message.Text += "\r\n主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始結束\r\n"; 16 }
1 private void btn_Sub_BeginInvoke_Click(object sender, EventArgs e) 2 { 3 txt_Message.Text = ""; 4 txt_Message.Text += "主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始工作\r\n"; 5 6 //開啟新的線程執行委托,主線程(UI線程)繼續向下執行 7 new Thread(() => 8 { 9 txt_Message.Text += "\r\n----子線程:" + Thread.CurrentThread.ManagedThreadId + "---開始工作\r\n"; 10 //在線程池中分配的子線程中執行委托方法,調用委托的線程繼續向下執行。 11 del.BeginInvoke(null, null); 12 txt_Message.Text += "\r\n----子線程:" + Thread.CurrentThread.ManagedThreadId + "---開始結束\r\n"; 13 }).Start(); 14 15 txt_Message.Text += "\r\n主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始結束\r\n"; 16 }
二、Control的Invoke、BeginInvoke
1、Control.Invoke (同步調用)
(1)在主線程(UI線程)中調用Control.Invoke
a、在主線程(UI線程)中調用Control.Invoke,先執行Invoke的方法,再執行Invoke后面的代碼。
(2)在子線程中調用Control.Invoke
a、子線程中調用Control.Invoke,子線程將調用的方法封裝成消息,調用API的RegisterWindowMessage()向UI窗口發送消息。 主線程繼續向下執行,子線程處於阻塞狀態。
b、當該消息被主線程執行完成后,子線程才能繼續往下執行。
2、Delegate.BeginInvoke (異步調用)
(1)在主線程(UI線程)中調用Control.BeginInvoke
a、在主線程(UI線程)中調用Control.BeginInvoke,將調用的方法封裝成消息,調用API的RegisterWindowMessage()向UI窗口發送消息。先執行Invoke后面的代碼,再執行Invoke的方法。
(2)在子線程中調用Control.BeginInvoke
a、子線程中調用Control.BeginInvoke,子線程將調用的方法封裝成消息,調用API的RegisterWindowMessage()向UI窗口發送消息。主線程繼續向下執行,子線程也繼續向下執行。
b、最后由主線程執行Invoke的方法。
3、Demo
a、在主線程(UI線程)中調用Control.Invoke
1 private void btn_Main_Invoke_Click(object sender, EventArgs e) 2 { 3 //執行順序:代碼A -> 代碼Invoke -> 代碼B 4 //都是在主線程中執行 5 6 txt_Message.Text = ""; 7 txt_Message.Text += "主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始工作\r\n"; 8 //代碼A 9 this.txt_Message.Invoke(del); 10 //代碼B 11 txt_Message.Text += "\r\n主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始結束\r\n"; 12 }
b、在子線程中調用Control.Invoke
1 private void btn_Sub_Invoke_Click(object sender, EventArgs e) 2 { 3 //執行順序: 4 //1、代碼A(主線程執行) 5 //2、代碼D(主線程執行) 和 代碼B(子線程執行) 並發執行 6 //3、封裝消息,並在UI線程中注冊消息(子線程執行) 7 //4、代碼Invoke(主線程執行) 8 //5、代碼C(子線程執行) 9 10 txt_Message.Text = ""; 11 txt_Message.Text += "主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始工作\r\n"; 12 //代碼A 13 new Thread(() => 14 { 15 //代碼B 16 int temp = 0; 17 this.txt_Message.Invoke(del); 18 //代碼C 19 temp = 1; 20 }).Start(); 21 //代碼D 22 txt_Message.Text += "\r\n主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始結束\r\n"; 23 }
c、在主線程(UI線程)中調用Control.BeginInvoke
1 private void btn_Main_BeginInvoke_Click(object sender, EventArgs e) 2 { 3 //執行順序:代碼A -> 封裝消息,並在UI線程中注冊消息 -> 代碼B -> 代碼Invoke 4 //都是在主線程中執行 5 6 txt_Message.Text = ""; 7 txt_Message.Text += "主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始工作\r\n"; 8 //代碼A 9 this.txt_Message.BeginInvoke(del); 10 //代碼B 11 txt_Message.Text += "\r\n主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始結束\r\n"; 12 }
c、在子線程中調用Control.BeginInvoke
1 private void btn_Sub_BeginInvoke_Click(object sender, EventArgs e) 2 { 3 //執行順序: 4 //1、代碼A(主線程執行) 5 //2、代碼D(主線程執行) 和 代碼B(子線程執行) 並發執行 6 //3、封裝消息,並在UI線程中注冊消息(子線程執行) 7 //4、代碼C(子線程執行) 8 //5、代碼Invoke(主線程執行) 9 10 txt_Message.Text = ""; 11 txt_Message.Text += "主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始工作\r\n"; 12 //代碼A 13 new Thread(() => 14 { 15 //代碼B 16 int temp = 0; 17 this.txt_Message.BeginInvoke(del); 18 //代碼C 19 temp = 1; 20 }).Start(); 21 //代碼D 22 txt_Message.Text += "\r\n主線程:" + Thread.CurrentThread.ManagedThreadId + "---開始結束\r\n"; 23 }