BeginInvoke 方法真的是新開一個線程進行非同步呼叫嗎?
參考以下代碼:
public delegate void treeinvoke();private void UpdateTreeView()
{
MessageBox.Show(System.Threading.Thread.CurrentThread.Name);
}private void button1_Click(object sender, System.EventArgs e)
{
System.Threading.Thread.CurrentThread.Name = "UIThread";
treeView1.BeginInvoke(new treeinvoke(UpdateTreeView));
}
看看運行結果,彈出的對話方塊中顯示的是 UIThread,這說明 BeginInvoke 所調用的委託根本就是在 UI 線程中執行的。
既然是在 UI 線程中執行,又何來“非同步執行”一說呢?
我們再看看下面的代碼:
public delegate void treeinvoke();private void UpdateTreeView()
{
MessageBox.Show(Thread.CurrentThread.Name);
}private void button1_Click(object sender, System.EventArgs e)
{
Thread.CurrentThread.Name = "UIThread";
Thread th = new Thread(new ThreadStart(StartThread));
th.Start();
}private void StartThread()
{
Thread.CurrentThread.Name = "Work Thread";
treeView1.BeginInvoke(new treeinvoke(UpdateTreeView));
}
再看看運行結果,彈出的對話方塊中顯示的還是 UIThread,這說明什嗎?這說明 BeginInvoke 方法所調用的委託無論如何都是在 UI 線程中執行的。
那 BeginInvoke 究竟有什麼用呢?
在多線程編程中,我們經常要在背景工作執行緒中去更新介面顯示,而在多線程中直接調用介面控制項的方法是錯誤的做法,具體的原因可以在看完我的這篇之後看看這篇:在多線程中如何調用Winform,如果你是大牛的話就不要看我這篇了,直接看那篇吧,反正那篇文章我沒怎麼看懂。
Invoke 和 BeginInvoke 就是為瞭解決這個問題而出現的,使你在多線程中安全的更新介面顯示。
正確的做法是將背景工作執行緒中涉及更新介面的代碼封裝為一個方法,通過 Invoke 或者 BeginInvoke 去調用,兩者的區別就是一個導致背景工作執行緒等待,而另外一個則不會。
而所謂的“一面響應操作,一面添加節點”永遠只能是相對的,使 UI 線程的負擔不至於太大而以,因為介面的正確更新始終要通過 UI 線程去做,我們要做的事情是在背景工作執行緒中包攬大部分的運算,而將對純粹的介面更新放到 UI 線程中去做,這樣也就達到了減輕 UI 線程負擔的目的了。
而在那段更新樹節點的代碼中,其實唯一起作用的代碼是:System.Threading.Thread.Sleep(100);,它使 UI 線程有了處理介面訊息的機會,其實 數位幽靈 將問題複雜化了,只要以下的代碼就可以很好的工作了。
private void button1_Click_(object sender, System.EventArgs e)
{
TreeNode tn; for(int i=0;i<100000;i++)
{
tn=new TreeNode (i.ToString()); this.treeView1.Nodes[0].Nodes.Add(tn); if (i%100 == 0) Application.DoEvents();
}
}