天天看點

WPF快速指導12: 線程處理模型

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線程去處理。代碼如下:

WPF快速指導12: 線程處理模型
WPF快速指導12: 線程處理模型

代碼

label1.Invoke(new MethodInvoker(delegate()

{

label1.Text = "temp";

}));

  而WPF的控件,我們找不到

這兩個方法了。因為WPF的UI線程都交給一個叫做排程器的類了。

     WPF 應用程式啟動時具有兩個線程:一個用于處理呈現,另一個用于管理 UI。 呈現線程實際上隐藏在背景運作,而 UI 線程則接收輸入、處理事件、繪制螢幕以及運作應用程式代碼。UI 線程在一個名為

Dispatcher

的對象中将工作項進行排隊。

根據優先級選擇工作項,并運作每一個工作項直到完成。

類提供兩種注冊工作項的方法:

。 這兩個方法都會安排執行一個委托。

是同步調用,即它直到 UI 線程實際執行完該委托時才傳回。

是異步調用,因而将立即傳回。

     上面的代碼在WPF中的實作如下:

WPF快速指導12: 線程處理模型
WPF快速指導12: 線程處理模型

Thread t = new Thread(new ThreadStart( delegate

{

tb_test.Dispatcher.Invoke(new Action(delegate

{

tb_test.Text = "123";

}), null);

}));

     注意,WPF中已經沒有MethodInvoker這個類,我們使用Action代替。當然,你也可以使用自定義的委托聲明。

    一些 WPF 應用程式需要多個頂級視窗。 一個線程/

組合管理多個視窗完全可以接受,但有時使用多個線程更佳。 特别是在其中一個視窗有可能獨占線程時,采用多個線程的優點更為突出。

    Windows 資料總管即采用這種工作方式。 每個新的資料總管視窗都屬于原始程序,但每個此類視窗都是在一個獨立線程的控制下建立的。該例子的簡單模仿如下代碼:

WPF快速指導12: 線程處理模型
WPF快速指導12: 線程處理模型

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();

多線程的異常處理,要采用特殊的做法。以下的處理方式會存在問題: 

WPF快速指導12: 線程處理模型
WPF快速指導12: 線程處理模型

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是屬于新起的異常,是以,正确的做法應該是: 

WPF快速指導12: 線程處理模型
WPF快速指導12: 線程處理模型

Thread t = new Thread((ThreadStart)delegate

}

MessageBox.Show("工作線程異常:" + error.Message + Environment.NewLine + error.StackTrace);

});

也就是說,新起的線程中異常的捕獲,可以将線程内部代碼全部try起來。原則上來說,每個線程自己的異常應該在自己的内部處理完畢,不過仍舊有一個辦法,可以将線程内部的異常傳遞到主線程。

在WPF窗體程式中,你可以采用如下的方法将工作線程的異常傳遞到主線程:

WPF快速指導12: 線程處理模型
WPF快速指導12: 線程處理模型

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。

WPF快速指導12: 線程處理模型

本文基于

Creative Commons Attribution 2.5 China Mainland License

釋出,歡迎轉載,演繹或用于商業目的,但是必須保留本文的署名

http://www.cnblogs.com/luminji

(包含連結)。如您有任何疑問或者授權方面的協商,請給我留言。

繼續閱讀