WPF快速指導12: 線程處理模型
本文摘要:
1:了解與UI相關的多線程操作;
2:多個視窗多個線程
3:WPF中的多線程異常
1:了解與UI相關的多線程操作
首先來說說傳統Winform。我們知道傳統Winform新起工作線程,在工作線程中不能對界面元素進行操作。如下面的代碼,運作會報錯“線程間操作無效: 從不是建立控件“label1”的線程通路它。”:
Thread t = new Thread(delegate()
{
label1.Text = "temp";
});
t.Start();
要使上面的代碼能成功運作,我們需要使用控件的
Invoke和
BeginInvoke和方法。這兩個方法的意思是說,讓指派這個行為交給UI線程去處理。代碼如下:
代碼
label1.Invoke(new MethodInvoker(delegate()
{
label1.Text = "temp";
}));
而WPF的控件,我們找不到
這兩個方法了。因為WPF的UI線程都交給一個叫做排程器的類了。
WPF 應用程式啟動時具有兩個線程:一個用于處理呈現,另一個用于管理 UI。 呈現線程實際上隐藏在背景運作,而 UI 線程則接收輸入、處理事件、繪制螢幕以及運作應用程式代碼。UI 線程在一個名為
Dispatcher的對象中将工作項進行排隊。
根據優先級選擇工作項,并運作每一個工作項直到完成。
類提供兩種注冊工作項的方法:
。 這兩個方法都會安排執行一個委托。
是同步調用,即它直到 UI 線程實際執行完該委托時才傳回。
是異步調用,因而将立即傳回。
上面的代碼在WPF中的實作如下:
Thread t = new Thread(new ThreadStart( delegate
{
tb_test.Dispatcher.Invoke(new Action(delegate
{
tb_test.Text = "123";
}), null);
}));
注意,WPF中已經沒有MethodInvoker這個類,我們使用Action代替。當然,你也可以使用自定義的委托聲明。
一些 WPF 應用程式需要多個頂級視窗。 一個線程/
組合管理多個視窗完全可以接受,但有時使用多個線程更佳。 特别是在其中一個視窗有可能獨占線程時,采用多個線程的優點更為突出。
Windows 資料總管即采用這種工作方式。 每個新的資料總管視窗都屬于原始程序,但每個此類視窗都是在一個獨立線程的控制下建立的。該例子的簡單模仿如下代碼:
private void NewWindowHandler(object sender, RoutedEventArgs e)
{
Thread newWindowThread = new Thread(new ThreadStart(ThreadStartingPoint));
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground = true;
newWindowThread.Start();
}
private void ThreadStartingPoint()
{
Window1 tempWindow = new Window1();
tempWindow.Show();
System.Windows.Threading.Dispatcher.Run();
多線程的異常處理,要采用特殊的做法。以下的處理方式會存在問題:
try
Thread t = new Thread((ThreadStart)delegate
throw new Exception("多線程異常");
t.Start();
}
catch (Exception error)
MessageBox.Show(error.Message + Environment.NewLine + error.StackTrace);
應用程式并不會在這裡捕獲線程t中的異常,而是會直接退出。從.NET2.0開始,任何線程上未處理的異常,都會導緻應用程式的退出(先會觸發AppDomain的UnhandledException)。上面代碼中的try-catch實際上捕獲的還是目前線程的異常,而t是屬于新起的異常,是以,正确的做法應該是:
Thread t = new Thread((ThreadStart)delegate
}
MessageBox.Show("工作線程異常:" + error.Message + Environment.NewLine + error.StackTrace);
});
也就是說,新起的線程中異常的捕獲,可以将線程内部代碼全部try起來。原則上來說,每個線程自己的異常應該在自己的内部處理完畢,不過仍舊有一個辦法,可以将線程内部的異常傳遞到主線程。
在WPF窗體程式中,你可以采用如下的方法将工作線程的異常傳遞到主線程:
throw new Exception("非窗體線程異常");
catch (Exception ex)
this.Dispatcher.Invoke((Action)delegate
throw ex;
});
WPF窗體程式的處理方式與Windows窗體程式比較,有兩個很有意思的地方:
第一個是,在Windows窗體中,我們采用的是BeginInvoke方法。你會發現使用Invoke方法,并不能引發主線程的Application.ThreadException。而在WPF窗體程式中,無論是排程器的Invoke還是BeginInvoke方法都能将異常傳遞給主線程。
第二個地方就是InnerException。WPF的工作線程異常将會抛到主線程,變成主線程異常的InnerException,而Windows窗體程式的工作線程異常,将會被吃掉,直接變為null,隻是在異常的Message資訊中儲存工作線程異常的Message。
本文基于
Creative Commons Attribution 2.5 China Mainland License釋出,歡迎轉載,演繹或用于商業目的,但是必須保留本文的署名
http://www.cnblogs.com/luminji(包含連結)。如您有任何疑問或者授權方面的協商,請給我留言。