先聲明,大部分資料均參考網上,進行了整理。
在 VS 中添加了 BackgroundWorker 組件, 該組件在多線程編程方面使用起來非常 方便,然而在開始時由於沒有搞清楚它的使用機制,
走了不少的彎路,現在把我 在使用它的過程中的經驗與諸位分享一下。
BackgroundWorker 類中主要用到的有這列屬性、方法和事件:
重要屬性:
1、CancellationPending 獲取一個值,指示應用程序是否已 請求取消后台操作。
通過在 DoWork 事件中判斷 CancellationPending 屬性可以 認定是否需要取消后台操作(也就是結束線程);
2、IsBusy 獲取一個值,指示 BackgroundWorker 是否正在運行異步操作。
程序中使用 IsBusy 屬性用來確定后 台操作是否正在使用中;
3、WorkerReportsProgress 獲取或設置一個值,該值指示 BackgroundWorker 能否報告進度更新
4、WorkerSupportsCancellation 獲取或設置一個值,該值指示 BackgroundWorker 是否支持異步取消。
設置 WorkerSupportsCancellation 為 true 使得程序可以調用 CancelAsync 方法提交終止掛起的后台 操作的請求;
重要方法:
1、CancelAsync 請求取消掛起的后台操作
2、RunWorkerAsync 開始執行后台操作
3、ReportProgress 引發 ProgressChanged 事件
重要事件:
1、DoWork 調用 RunWorkerAsync 時發生
2、ProgressChanged 調用 ReportProgress 時發生
3、RunWorkerCompleted 當后台操作已完成、被取消或引發異常時發生 另外還有三個重要的參數是
RunWorkerCompletedEventArgs 以及 DoWorkEventArgs、ProgressChangedEventArgs。
BackgroundWorker 的各屬性、方法、事件的調用機制和順序:
從上圖可見在整個生活周期內發生了 3 次重要的參數傳遞過程:
參數傳遞 1:此次的參數傳遞是將 RunWorkerAsync(Object)中的 Object 傳遞到DoWork 事件的
DoWorkEventArgs.Argument,由於在這里只有一個參數可以 傳遞,所以在實際應用往封裝一個類,
將整個實例化的類作為 RunWorkerAsync 的 Object 傳遞到 DoWorkEventArgs.Argument;
參數傳遞 2:此次是將程序運行進度傳遞給 ProgressChanged 事件,
實際使用中往往使用給方法和事件更新進度條或者日志信息;
參數傳遞 3:在 DoWork 事件結束之前,將后台線程產生的結果數據賦給
DoWorkEventArgs.Result 一邊在 RunWorkerCompleted 事件中
調用 RunWorkerCompletedEventArgs.Result 屬性取得后台線程產生的結果。
另外從上圖可以看到 DoWork 事件是在后台線程中運行的,
所以在該事件中 不能夠操作用戶界面的內容,如果需要更新用戶界面,
可以使用 ProgressChanged 事件及 RunWorkCompleted 事件來實現。
2. 凡是 WinForm 的應用程序,如果他執行了一個的非常冗長的處理操作(比如文件 查詢),
它在執行時會鎖定用戶界面,雖然主活動窗口 一直在運行,但用戶無 法與程序交互,
無法移動窗體或改變窗體大小,所以用戶感覺很不爽。如何做才 能使得這個程序有響應。
答案就是在后台線程中執行這個操作。
在這里已經有了多種方法來做這個事情:
(一) 委托異步調用 將具體耗時的操作作為一個委托,並用 BeginInvoke 來異步執行這個委托
(Invoke 是同步調用),並且可以為這個操作傳入參數並且通過 EndInvoke 方 法獲得返回返回值。
(二) 使用 ThreadPool 新建.net FrameWork 中自帶的 WaitCallback 委托,然后放到線程池
中運行 ThreadPool.QueueUserWorkItem( callback ); 根據 WaitCallback 委托的定義,
可以傳入一個 object 類型的參數。 但是不能精確的控制線程池中的線程。
(三) 使用 Thread 和 ThreadPool 相比,使用 Thread 的開銷會比較大。但是它有它的優勢,
使用 Thread 類可以顯式管理線程。只要有可能,就應該使用 ThreadPool 類來 創建線程。
然而,在一些情況下,您還是需要創建並管理您自己的線程,而不是 使用 ThreadPool 類。
在.net 2.0 中,提供了一個新的委托 ParameterizedThreadStart 支持啟動一個線程並傳入參數,
這是對原來的 ThreadStart 委托的改進。
說了這么多還沒有說到今天的主角 BackgroundWorker,他也是一個在 2.0 中 新增的類,可以用於啟動后台
線程,並在后台計算結束后調用主線程 的方法.可 以看出同樣的功能使用委托的異步調用也可以實現,只是
使用 BackgroundWorker 的話會更加的簡便快捷,可以節省開發時間,
並把你從 創建 自己的委托以及對它們的調用中解救出來。真是這樣的嗎看看下面這個例子。
其 實我也是從 101Samples 中看到的例子。
先看看 BackgroundWorker 中的主要概念。
第一:主要的事件及參數。
DoWork——當執行 BackgroundWorker.RunWorkerAsync 方法時會觸 發該事件,
並且傳遞 DoWorkEventArgs 參數;
ProgressChanged——操作處理中獲得的處理狀態變化,
通過 BackgroundWorker.ReportProgress(int)方法 觸發該事件,
並且傳遞 ProgressChangedEventArgs,其中包含了處理的百分比;
RunWorkerCompleted ——異步操作完成后會觸發該事件,當然如果需要在操作
過程中結束可以執行 BackgroundWorker.CancelAsync 方法要求異步調用中 止,
並且在異步委托操作中檢測 BackgroundWorker.CancellationPending 屬性如果 為 true 的話,
跳出異步調用,同時將 DoWorkEventArgs.Cancel 屬性設為 true,這樣當退出異步調用的時候,
可以讓處理 RunWorkerCompleted 事件的函數 知道 是正常退出還是中途退出。
第二:主要的方法。
BackgroundWorker.RunWorkerAsync—— “起動”異步調用的方法有兩次重載
RunWorkerAsync(),RunWorkerAsync(object argument),第二個重載提供了一個 參數,
可以供異步調用使用。(如果有多個參數要傳遞怎么辦,使用一個類來傳 遞他們吧)。
調用該方法后會觸發 DoWork 事件,並且為處理 DoWork 事件的函數 DoWorkEventArg 事件
參數,其中包含了 RunWorkerAsync 傳遞的參數。在相應 DoWork 的處理函數中就可以做具體的復雜操作。
BackgroundWorker.ReportProgress—— 有時候需要在一個冗長的操作中向用戶不斷反饋進度,這樣的話就可 以調用的 ReportProgress(int percent),在調用 ReportProgress 方法時,觸 發 ProgressChanged 事件。提供一個在 0 到 100 之間的整數,它表示后台活動 已完成的百分比。你也可能提供任何對象作為第二個參數,允許你 給事件處理 程序傳遞狀態信息。作為傳遞到此過程的 ProgressChangedEventArgs 參數屬 性, 百分比和你自己的對象 (如果提供的話) 均要被傳遞到 ProgressChanged 事 件處理程序。這些屬性被分別命名為 ProgressPercentage 和 UserState,並且 你的事件處理程序可以以任何需要的方式使用它們。(注意:只有在 BackgroundWorker.WorkerReportsProgress 屬性被設置為 true 該方法才可用)。
BackgroundWorker.CancelAsync—— 但需要退出異步調用的時候,就調用的這個方法。但是樣還不夠,因為它僅 僅是將 BackgroudWorker.CancellationPending 屬 性設置為 true。你需要在具 體的異步調用處理的時候,不斷檢查 BackgroudWorker.CancellationPending 是否為 true,如果是真的話就退出。(注意:只有在 BackgroundWorker.WorkerSupportsCancellation 屬性被設置為 true 該方法才 可用)。
具體代碼示例可以在博客中找到!