WPF與WinForm對比談 多線程編程優(yōu)化是關鍵
51CTO之前也曾報道過《Visual Studio 2010 RC中在WPF開發(fā)方面有多個不同之處》,那么看來WPF還是有其可取之處,希望本文能對大家有所幫助。
很多人問過我這樣一個問題:WPF和以前的WinForm有什么區(qū)別?
我之前的回答一直是:沒什么區(qū)別,僅僅是表示層用XAML封了層皮,使得Windows看起來更炫了。
今天(確切的說是昨天),我終于發(fā)現(xiàn)了我膚淺。首先我要澄清一下,WPF較之WinForm的先進之處不止一點點。
對于WPF,很多人都以為這是微軟的一個小玩具,充其量就是讓Vista和Win7的表示層更炫了,然后就會吸引人們去購買——當然了,我相信多數(shù)人購買Wim7的動機并不在乎它的內核做了什么變動...
言歸正傳...
WPF將Windows表示層發(fā)展至用聲明式語言進行開發(fā),并且融入大量的動畫和特效,使得在Win32中極難做到的富客戶端應用能在WPF中信手拈來,同時用矢量圖取代位圖,引入路由事件(RoutedEvent)對元素樹進行多層監(jiān)聽,通過依賴屬性(DependencyProperty)動態(tài)變更控件樹等,這些都是表面上我們所看到的。不過這些不是我今天想說的重點,何況有不少牛人的研究要比我深入多了,實在是自慚形穢啊,今天我只是承認錯誤來的。
在眾多WPF對于WinForm的優(yōu)勢中,令我意識到我之前的錯誤的一點就是WPF對于多線程編程的優(yōu)化。
在WinForm程序開發(fā)時,一旦涉及多線程操作,我們一般不可能沒有見過InvalidOperationException這個異常。這個異常的出現(xiàn)多數(shù)情況是由于worker線程(子線程)修改主線程控件(或對象)的屬性而導致的非法操作,當然這種做法也非每次都會失敗,這主要取決于子線程想要操作的對象是不是線程安全的。
無論是Windows窗體還是WPF,問題的成因都很簡單:Windows控件使用的是組件對象模型(Component Object Model,COM)單線程單元(Single-threaded Apartment,STA)模型,因為其底層的控件是單元線程(apartment-threaded)的。此外,很多控件都用消息泵(message pump)來完成操作。因此,這種模型就需要所有調用該控件的方法都和創(chuàng)建該控件的方法位于同一個線程上。
WinForm控件提供了InvokeRequired屬性來判斷當前線程是不是創(chuàng)建此控件的線程。一旦控件創(chuàng)建完成,那么InvokeRequired的效率將會不錯,且也能保證安全。不過若是目標控件尚未被創(chuàng)建(此時,雖然C#對象已經存在,不過其底層的窗口句柄仍舊為null),那么InvokeRequired則可能會耗費比較長的時間。(它會從下至上遍歷整個控件樹,直到找到一個可以承載此控件且已經實例化并被創(chuàng)建了的父控件,這種做法可以保證子控件將會與父控件在同一個線程上創(chuàng)建。找到合適的父控件之后,框架即可執(zhí)行同樣的檢查,比較當前線程的ID和創(chuàng)建該父控件的線程的ID。)。若是框架無法找到任何一個已創(chuàng)建的父窗體,那么則需要找到一些其他類型的窗體。若在層次體系中無法找到可用的窗體,那么框架將開始尋找暫存窗體(parking window),暫存窗體讓你不會被某些Win32 API奇怪的行為所干擾。簡而言之,有些對窗體的修改(例如修改某些樣式)需要銷毀并重新創(chuàng)建該窗體。暫存窗體就是用來在父窗體被銷毀并重新創(chuàng)建的過程中用來臨時保存其中的控件的。在這段時間內,UI線程僅運行于暫存窗體中。
“通常,WPF 應用程序從兩個線程開始:一個用于處理呈現(xiàn),一個用于管理 UI。呈現(xiàn)線程有效地隱藏在后臺運行,而 UI 線程則接收輸入、處理事件、繪制屏幕以及運行應用程序代碼。”
“UI 線程對一個名為 Dispatcher 的對象內的工作項進行排隊。 Dispatcher 基于優(yōu)先級選擇工作項,并運行每一個工作項,直到完成。每個 UI 線程都必須至少有一個 Dispatcher,并且每個 Dispatcher 都只能在一個線程中執(zhí)行工作項。”
——MSDN
WPF開始設計的時候,就將多線程的問題考慮了進去,上述很多過程都得到了簡化。這得益于Dispatcher類的使用,每個線程都有一個Dispatcher。在第一次訪問某個控件的Dispatcher時,類庫將察看該線程是否已經擁有了Dispatcher。若已經存在,那么直接返回。如果沒有的話,那么將創(chuàng)建一個新的Dispatcher對象,并關聯(lián)在控件及其所在的線程之上。Dispatcher提供了類似InvokeRequired的方法(CheckAccess)【實際上并不提倡使用此方法,且在目前的WPF中此方法已經被取消】。這個方法只是比較線程的ID,所以會很快。
另外,Dispatcher提供了優(yōu)先隊列(總共11個Priority,主要是用于WPF中UI的層次結構設計,比如動畫的優(yōu)先級就是最高的);異步調用(WinForm中的Control.BeginInvoke和Control.EndInvoke也提供異步功能);DispatcherTimer (使用 DispatcherTimer 而不是使用 System.Timers.Timer 的原因是 DispatcherTimer 與 Dispatcher 運行于相同的線程,并且可以在 DispatcherTimer 上設置 DispatcherPriority),簡化了開發(fā)多線程GUI程序。
WPF延伸閱讀
WPF框架圖
WPF為Windows Presentation Foundation的縮寫 ,其原來代號為“Avalon”,因“我佩服”拼音首字母組合一樣,國內有人調侃地稱之為“我佩服”。WPF是微軟新一代圖形系統(tǒng),運行在.NET Framework 3.0架構下,為用戶界面、2D/3D 圖形、文檔和媒體提供了統(tǒng)一的描述和操作方法?;贒irectX 9/10技術的WPF不僅帶來了前所未有的3D界面,而且其圖形向量渲染引擎也大大改進了傳統(tǒng)的2D界面,比如Vista中的半透明效果的窗體等都得益于WPF。 程序員在WPF的幫助下,要開發(fā)出媲美Mac程序的酷炫界面已不再是遙不可及的奢望。 WPF相對于Windows客戶端的開發(fā)來說,向前跨出了巨大的一步,它提供了超豐富的.NET UI 框架,集成了矢量圖形,豐富的流動文字支持flow text support,3D視覺效果和強大無比的控件模型框架。
WinForm延伸閱讀
WinForm是.Net開發(fā)平臺中對Windows Form的一種稱謂。.Net 為開發(fā)WinForm的應用程序提供了豐富的Class Library(類庫)。這些WinForm 類庫支持RAD(快速應用程序開發(fā)),這些類庫被封裝在一個名稱空間之中,這個名稱空間就是System.Windows.Forms。在此名稱空間中定義了許多類,在開發(fā)基于.Net的GUI應用程序的時候,就是通過繼承和擴展這些類才使得我們的程序有著多樣的用戶界面。
原文標題:關于WPF,我個人長期的誤解
鏈接:http://www.cnblogs.com/iamzhyk/archive/2010/04/07/1705903.html
【編輯推薦】