Silverlight的多線程能力(上)

來源: infoq  發布時間: 2011-03-10 16:23  閱讀: 1452 次  推薦: 0   原文鏈接   [收藏]  

  對于多線程其實一直以來都存在很多誤區:比如多任務與多線程就很容易被混為一談,而多線程也常被理所應當的認為是并行等等。而事實卻是:多任務≠多線程、單任務≠單線程、多線程不一定并行,多線程與性能不成線性關系等等,其中道理在這里不再詳述。筆者認為Silverlight多線程主要作用不是在于提高性能,而是在于用戶體驗,其根本目的是解決用戶體驗中的響應速度,減少單線程帶來的阻塞問題。用一個貼切的例子來形容單線程和多線程的區別:單線程就好像只有一個服務窗口賣票的車站,人們排隊買票時都是單線程處理的,而且不能搶奪位置,這樣只要前方有一個人出現長時間等待,后面的人都不能被響應,這就出現了單線程阻塞;而多線程就好像有多個服務窗口去賣票,這樣車票買賣和等待的情況就會好很多(當然這個例子如果換成公共廁所,對于用戶體驗就顯得更為重要了)。

  這次我們就要來看看Silverlight的多線程能力,其實Silverlight的多線程體現在兩大方面:

  第一方面是將UI線程與后臺工作線程的分離,使得UI線程可以更好地響應用戶操作,而后臺線程處理完后,允許通過異步的方式將處理結果推回前臺進行展示。筆者認為這是多線程在Silverlight中最主要的作用(很多傳統Web應用開發者在剛開始接觸Silverlight時很不適應這種前后臺線程的異步操作)。

  第二方面是對后臺作業的多線程支持,比如當需要在客戶端后臺并行運算時,你可以通過發起多個線程來完成這些運算。在上期《Silverlight CoreCLR結構淺析》中,我已經給大家介紹了Silverlight的基礎類庫,其中就包括多線程的相關類集。

  UI線程是Silverlight與用戶交互的線程,在Silverlight中UI線程是單一的,其中裝入的是UI控件類及用于數據綁定的View Model類(什么是View Model?就是只為View層服務的實體,如果要展開說會很長,就此打住!),而在后臺線程中是不能直接訪問這些UI線程中的數據與控件對象的屬性。但大家不用擔心,Silverlight和WPF的線程模型都使用了類似于Java Swing中EDT(Event Dispatch Thread)這種安全的事件分發線程模型來解決UI線程與其他后臺線程的數據互訪問題。在Silverlight(WPF)的控件類庫System.Windows下所有類都繼承了DependencyObject基類,DependencyObject類不僅提供了Silverlight(WPF)最基礎的依賴性屬性服務(什么是依賴性屬性?簡單的說就是對象屬性值依賴于其他計算值的方式,這種方式為數據綁定、動畫、重用樣式都提供了可行性,這里不再展開),同時也開啟了UI線程與后臺線程的數據互訪通道,在DependencyObject中有一個非常重要的屬性——Dispatcher,后臺線程可以通過調用發起者(一般都是UI控件)的Dispatcher來實現互操作,后臺線程可以通過下面的方式來直接操作UI線程中的對象:

 
_UISender.Dispatcher.BeginInvoke(() =>
{
//這里可以訪問UI線程中的對象,因為這個委托本身就在UI線程中執行
}

  上面的()=>是Lamda表達式中對于無入參的委托方法的簡寫形式,如果有傳入參數可以在括號中列明,當然你也可以使用Action各種重載到其他地方實現委托過程。

  如果要實現UI線程創建并訪問后臺線程就更加簡單,Silverlight提供了多種創建后臺線程的方式:

Thread類是最基礎的多線程類,它可以創建一個獨立運行的線程,比如:

  • 基于普通的System.Threading.Thread類創建后臺線程
 
Threadthread = newThread(obj.functionName);
thread.IsBackground
= true;
thread.Start();

但Thread對于線程的監控、銷毀、回調都比較復雜,因此筆者往往使用Thread來完成一些簡單的且不需要回調的任務。

DispatchTimer類是Silverlight(WPF)里才出現的后臺線程定時器,相較于原有System.Threading.Timer差別在于DispatchTimer是真正的在后臺線程內獨立執行,而Timer仍然在UI線程中執行,只是定時獲得UI線程控制權而已。DispatchTimer只適合于定時執行的任務,你可以根據需要來設置等待時歇,其創建方式如下:

  • 基于System.Threading.DispatchTimer類創建后臺定時器線程
 
DispatcherTimer dt = newDispatcherTimer();
dt.Interval
= newTimeSpan(0, 0, 0, 0, 10);
dt.Tick
+= newEventHandler(dt_Tick);
dt.Start();

void dt_Tick(object sender, EventArgs e)
{

//超過等待時歇時發生
//這里可以訪問UI線程中的對象
//如果要結束定時器可以調用dt.Stop();
}

DispatcherTimer其實也是除StoryBoard外可以實現動畫的重要組件,當然要慎用DispatcherTimer來構建過多后臺線程,否則會使CPU調度開銷增加反而影響效率!(調度開銷將在下部分講解)

微軟在WinForm架構中就引入了BackgroundWorker類,這個類內建了許多線程包裝方法,從而大大簡化線程交互的編碼過程。在Silverlight(WPF)中也可以通過BackgroundWorker類來輕松創建后臺線程,其創建方式如下:

  • 基于System.ComponentModel.BackgroundWorker類輕松創建后臺線程
 
BackgroundWorker bw = newBackgroundWorker();
bw.DoWork
+= newDoWorkEventHandler((object, doworkeventarg) =>obj.function());
bw.RunWorkerCompleted
+= newRunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
if(!bw.IsBusy) bw.RunWorkerAsync();
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{

//任務完成時回調事件
}

BackgroundWorker也可以通過ReportProgress(intpercentProgress)方法來向其他線程報告其進度完成情況,當然這只適合于可量化進度的后臺工作線程,其實現如下:

 
//后臺線程obj.function()中

obj.ReportProgress(i);

//在UI線程中定義報告時間委托
bw.ProgressChanged += newProgressChangedEventHandler(bw_ProgressChanged);
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{

//顯示進度的相關處理方法
}

在所有多線程解決方案中,ThreadPool線程池是筆者最常用的技術。其優點在于易于控制并且減少開銷,在線程池中的線程不會由于完成一個任務就消亡,而是會繼續執行其他的任務,大大減少了線程的創建與銷毀開銷。ThreadPool中的QueueUserWorkItem方法可以將任何處理函數排入后臺線程隊列中執行,其創建方式也非常簡單:

  • 基于System.Threading.ThreadPool靜態類創建后臺線程
 
obj.OnEvent += (object, eventarg) => Dispatcher.BeginInvoke(UI_OnEvent);
ThreadPool.QueueUserWorkItem(state
=>obj.function(), stat);
voidUI_OnEvent()
{

//后臺線程事件發生時的回調事件
}

后臺線程對象obj中你可以隨意定義回調事件,并通過Dispatcher.BeginInvoke的方法來通知UI線程的委托。這樣的方式比較簡單而且實用。當然ThreadPool類還提供了RegisterWaitForSingleObject方法來實現Timer定時器的功能,可以說ThreadPool是在企業應用中比較常用的多線程實現類。

  至此,就給大家介紹了Silverlight常用多線程實現方式,但筆者還要強調的是:Silverlight的多線程是為了提升用戶體驗。其實Silverlight開發圍繞的關鍵是用戶體驗,用戶體驗在現代商業應用開發中的地位非常的重要。本主題的下半部分,筆者將通過一個實例來為大家講述Silverlight的多線程性能,以及與其他Web開發技術的性能對比。

0
0
 
標簽:Silverlight
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()