C#學習筆記之 invoke與BeginInvoke
前段時間在寫C#的上位機,用到invoke和BeginInvoke。前面對這兩個的用法和原理比較模糊,這幾天參考了網上的一些資料,整理如下筆記。
1.1 invoke與BeginInvoke介紹
invoke與BeginInvoke的兩種使用情況:
1.control中的invoke、BeginInvoke
2.delegrate中的invoke、BeginInvoke
這兩種情況是不同的,這裏主要介紹第一種。dotNET中對invoke和BeginInvoke的官方定義如下:
control.invoke(參數delegate)方法:在擁有此控件的基礎窗口句柄的線程上執行指定的委託
control.BeginInvoke(參數delegate)方法:在創建此控件的基礎窗口句柄的線程上異步執行指定的委託
其中Control中的invoke和BeginInvoke的參數爲delegate,委託的方法是在Control的線程上執行的,即UI線程。由定義我們可以知道invoke表示同步、Begininvoke表示異步
下面做一個測試:
新建一個UI工程,界面如下圖1
1.2 invoke分析:
invoke代段碼如下:
private void invoke_btn_Click(object sender, EventArgs e)
{
//主線程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "AAA");
invokeThread = new Thread(new ThreadStart(StartMethod));
invokeThread.Start();
string ch = string.Empty;
for(int ss = 0; ss < 3; ss++)
{
Thread.Sleep(1000);
ch = ch + "B";
}
//主線程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + ch);
}
private void StartMethod()
{
//子線程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "CCC");
//提交到主線程
invoke_btn.Invoke(new InvokeDelegate(invokeMethod));
//子線程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "DDD");
}
private void invokeMethod()
{
//主線程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "EEE");
}
測試結果:雙擊運行exe,單擊invoke,程序運行的界面:1AAA->3CCC和1BBB->1EEE->3DDD。(Debug模式下的HashCode可能不一樣,但是運行的順序是一樣的)
結果分析:單擊invoke按鈕後,執行invoke_btn_Click函數,主線程運行AAA,然後BBB和子線程CCC同時執行(這裏用循環+延時,顯示更加清楚),接着通過invoke來將invokeMethod方法提交給主線程,然後子線程等待主線程執行完畢(即等待EEE執行完成),最後執行子線程的DDD。
1.3 BeginInvoke分析:
BeginInvoke代段碼如下:
private void bginvoke_btn_Click(object sender, EventArgs e)
{
//主線程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "AAA");
bginvokeThread = new Thread(new ThreadStart(bgStartMethod));
bginvokeThread.Start();
string ch = string.Empty;
for (int ss = 0; ss < 3; ss++)
{
Thread.Sleep(1000);
ch = ch + "B";
}
//主線程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + ch);
}
private void bgStartMethod()
{
//子線程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "CCC");
//提交給主線程
bginvoke_btn.BeginInvoke(new InvokeDelegate(bginvokeMethod));
//子線程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "DDD");
}
private void bginvokeMethod()
{
//主線程
MessageBox.Show(Thread.CurrentThread.GetHashCode().ToString() + "EEE");
}
測試結果:雙擊運行exe,單擊BeginInvoke,程序運行的界面:1AAA->3CCC和1BBB->1EEE和3DDD。(Debug模式下的HashCode可能不一樣,但是運行的順序是一樣的)
結果分析:單擊BeginInvoke按鈕後,執行bginvoke_btn_Click函數,主線程運行AAA,然後BBB和子線程CCC同時執行,然後通過BeginInvoke來將bginvokeMethod方法提交給主線程,接着主線程執行EEE(主線程自己的任務完成),同時子線程繼續執行DDD。
1.4對比分析
通過上述兩個測試可以發現:invoke和BeginInvoke提交的委託方法都是在主線程中執行的。但是invoke所提交的委託方法是(EEE)執行完畢後,才繼續執行的DDD;而BeginInvoke提交的委託方法後,子線程可以繼續執行DDD,不需要等待EEE執行完畢。因此在兩者的使用方面是不同的,當後臺線程在更新一個UI控件的狀態後不需要等待,而是繼續往下執行,此時宜用BeginInvoke來進行處理,如一邊接收數據,將數據存入隊列,一邊對數據隊列進行文本,曲線的更新;當後臺線程需要操作UI控件,並且需要等待該操作執行完畢才能繼續往下執行,此時宜用invoke來進行處理,如必須接收一幀立即處理一幀數據。