日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

编写高质量代码改善C#程序的157个建议——建议87:区分WPF和WinForm的线程模型...

發布時間:2025/7/14 C# 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 编写高质量代码改善C#程序的157个建议——建议87:区分WPF和WinForm的线程模型... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

建議87:區分WPF和WinForm的線程模型

WPF和WinForm窗體應用程序都有一個要求,那就是UI元素(如Button、TextBox等)必須由創建它的那個線程進行更新。WinForm在這方面的限制并不是很嚴格,所以像下面這樣的代碼,在WinForm中大部分情況下還能運行(本建議后面會詳細解釋為什么會出現這種現象):

private void buttonStartAsync_Click(object sender, EventArgs e) { Task t = new Task(() =>{ while (true) { label1.Text = DateTime.Now.ToString(); Thread.Sleep(1000); } }); //如果有異常,就啟動一個新任務 t.ContinueWith((task) =>{ try { task.Wait(); } catch (AggregateException ex) { foreach (Exception inner in ex.InnerExceptions) { MessageBox.Show(string.Format("異常類型:{0}{1}來自:{2}{3}異常內容:{4}", inner.GetType(),Environment.NewLine, inner.Source, Environment.NewLine, inner.Message)); } } }, TaskContinuationOptions.OnlyOnFaulted); t.Start(); }

但是,相同的一段代碼如果放到WPF環境中,就肯定會拋出System.InvalidOperationException異常。


理論上,WinForm和WPF的線程模型非常接近,它們最后都是調用API(GetMessage或PeekMessage)來處理其他線程發送過來的消息,這些消息存儲在系統的一個消息隊列中。在WinForm和WPF中,創建主界面的線程就是主線程,也就是UI線程,UI線程負責處理該消息隊列。只是兩者在處理消息隊列的上層機制上稍微有一些不同,這就造成了同樣的代碼得到不同的結果。

在WinForm框架中有一個ISynchronizeInvoke接口,所有的UI元素(表現為Control)都繼承了該接口。其中,接口中的InvokdRequired屬性表示了當前線程是否是創建它的線程。接口中的Invoke和BeginInvoke方法負責將消息發送到消息隊列中,這樣,UI線程就能夠正確處理它了。那么,上面的這段代碼在WinForm上的改進版本為(僅列出While循環部分):

while (true) { if (label1.InvokeRequired) label1.BeginInvoke(new Action(() =>{ label1.Text = DateTime.Now.ToString(); })); else label1.Text = DateTime.Now.ToString(); Thread.Sleep(1000); }

BeginInvoke方法接受的是一個Delegate類型的參數,在這里我們用一個Action來實現。

WPF應用程序的線程模型則完全依賴于DispatcherObject類型。所有的WPF控件都繼承自一個抽象類Visual,而這個抽象類又最終繼承自DispatcherObject類型。在這個DispatcherObject類型中有一個屬性,兩個方法。屬性Dispatcher完成所有的工作線程和UI線程之間的調度任務。CheckAccess方法負責檢測工作線程是否可以訪問控件,如果是,則返回True;否則返回False。VerifyAccess方法則負責檢測工作線程是否具有控件的訪問權限,如果不能訪問則拋出異常InvalidOperationException。

WinForm應用程序用類似CheckAccess的方式進行訪問權限的判斷;WPF應用程序則進行了改進,所有的UI控件都采用VerifyAccess的方式進行工作線程訪問權限的判斷。這直接決定了本建議開頭處那個例子的輸出,WPF只要判斷出工作線程和UI線程不是同一個線程的,則直接拋出異常,而WinForm卻有成功執行的余地。但是,WinForm的這種機制直接造成了程序的不穩定,因為即使在大部分情況下代碼能很好的工作,可是在不確定的情況下,那樣的代碼中工作線程會直接操作UI元素,這樣還是會拋出異常的。

考慮到WinForm在這個問題上的局限性,再次對WinForm的線程模型處理進行改進:

//用于表示主線程,在本例中就是UI線程 Thread mainThread; bool CheckAccess() { return mainThread == Thread.CurrentThread; } void VerifyAccess() { if (!CheckAccess()) throw new InvalidOperationException("調用線程無法訪問此對象,因為另一個線程擁有此對象"); } private void buttonStartAsync_Click(object sender, EventArgs e) { //當前線程就是主線程 mainThread = Thread.CurrentThread; Task t = new Task(() =>{ while (true) { if (!CheckAccess()) label1.BeginInvoke(new Action(() =>{ label1.Text = DateTime.Now.ToString(); })); else label1.Text = DateTime.Now.ToString(); Thread.Sleep(1000); } }); //如果有異常,就啟動一個新任務 t.ContinueWith((task) =>{ try { task.Wait(); } catch (AggregateException ex) { foreach (Exception inner in ex.InnerExceptions) { MessageBox.Show(string.Format("異常類型:{0}{1}來自:{2}{3}異常內容:{4}", inner.GetType(), Environment.NewLine, inner.Source, Environment.NewLine, inner.Message)); } } }, TaskContinuationOptions.OnlyOnFaulted); t.Start(); }

?

在這段代碼中,我們模擬WPF中DispatcherObject的兩個方法CheckAccess和VerifyAccess對線程模型進行了重新處理,增強了系統的穩定性。在實際工作中,我們也可以提取這兩個方法為擴展方法,以便項目中的所有UI類型都能使用到。

WPF支持這兩個方法,其全部代碼如下所示(注意查看While循環部分):

private void buttonStart_Click(object sender, RoutedEventArgs e) { Task t = new Task(() =>{ while (true) { this.Dispatcher.BeginInvoke(new Action(() =>{ textBlock1.Text = DateTime.Now.ToString(); })); Thread.Sleep(1000); } }); //為了捕獲異常,啟動了一個新任務 t.ContinueWith((task) =>{ try { task.Wait(); } catch (AggregateException ex) { foreach (Exception inner in ex.InnerExceptions) { MessageBox.Show(string.Format("異常類型:{0}{1}來自:{2}{3}異常內容:{4}", inner.GetType(), Environment.NewLine, inner.Source, Environment.NewLine, inner.Message)); } } }, TaskContinuationOptions.OnlyOnFaulted); t.Start(); }

?


注意 為了演示方便,本建議中的異常沒有傳遞到主線程。在實際編碼中,應當始終考慮將異常包裝到主線程。

?

?

轉自:《編寫高質量代碼改善C#程序的157個建議》陸敏技

轉載于:https://www.cnblogs.com/jesselzj/p/4743508.html

總結

以上是生活随笔為你收集整理的编写高质量代码改善C#程序的157个建议——建议87:区分WPF和WinForm的线程模型...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。