C# BackgroundWorker 詳解

来源:http://www.cnblogs.com/sparkdev/archive/2016/09/25/5906272.html
-Advertisement-
Play Games

在C#程式中,經常會有一些耗時較長的CPU密集型運算,如果直接在 UI 線程執行這樣的運算就會出現UI不響應的問題。解決這類問題的主要途徑是使用多線程,啟動一個後臺線程,把運算操作放在這個後臺線程中完成。但是原生介面的線程操作有一些難度,如果要更進一步的去完成線程間的通信就會難上加難。 還好 .NE ...


在C#程式中,經常會有一些耗時較長的CPU密集型運算,如果直接在 UI 線程執行這樣的運算就會出現UI不響應的問題。解決這類問題的主要途徑是使用多線程,啟動一個後臺線程,把運算操作放在這個後臺線程中完成。但是原生介面的線程操作有一些難度,如果要更進一步的去完成線程間的通信就會難上加難。

還好 .NET 類庫中提供了一個叫做 BackgroundWorker 的類可以比較優雅的解決這類問題。雖然BackgroundWorker 類使用起來比較簡單,但其中還是有一些需要註意的細節,下麵我們就通過 demo 程式介紹它的主要用法。我們在 demo中計算1到100的累加和,為了演示,每次計算都 sleep 600毫秒,demo 的UI為:

用法概述

在窗體上構建一個BackgroundWorker 實例,在它的 DoWork事件處理函數中添加耗時的運算,然後調用它的RunWorkerAsync方法就可以了。

private BackgroundWorker _demoBGWorker = new BackgroundWorker();
_demoBGWorker.DoWork += BGWorker_DoWork;
_demoBGWorker.RunWorkerAsync();
private void BGWorker_DoWork(object sender, DoWorkEventArgs e)
{
    //在這裡執行耗時的運算。
    int sum = 0;
    for (int i = 0; i <= 100; i++)
    {
        sum += i;
    }
}

是不是有點太簡單了?那麼讓我們考慮下麵的問題:

如果我們想要把參數傳遞給運算過程該怎麼做?
在運算過程中我們希望把實時的信息顯示在UI上該怎麼辦?
如果我們想要取消正在進行的運算該怎麼辦?
如果運算過程出現異常我們又該如何處理?

接下來我們就一個一個的處理這些問題。

把參數傳遞給運算過程

直接把100寫死到運算過程中可不好,我們還打算允許用戶指定求和的範圍呢!所以需要把100作為參數傳遞給計算過程。在概述中我們通過調用RunWorkerAsync方法啟動計算過程,其實這個方法可以接受一個 object 類型的參數。通過它我們就可以把任何數據傳遞給計算過程:

//別忘了設置滾動條。
this.progressBarSum.Maximum = 100;
_demoBGWorker.RunWorkerAsync(100);
//下麵是更新後的 BGWorker_DoWork 方法:
private void BGWorker_DoWork(object sender, DoWorkEventArgs e)
{
    //在這裡執行耗時的運算。
    int endNumber = 0;
    if(e.Argument != null)
    {
        endNumber = (int)e.Argument;
    }
    int sum = 0;
    for (int i = 0; i <= endNumber; i++)
    {
        sum += i;
    }
}

BGWorker_DoWork事件處理函數通過參數 e 的Argument屬性傳來了我們期望的運算信息。

把消息傳遞給UI

由於計算過程比較長,我們在通過進度條來顯示當前進度的同時,還希望能實時的把計算的中間結果顯示在UI上。當然,BackgroundWorker對這個用例也提供了很好的支持。它允許我們在執行計算的過程中給UI線程發送消息,下麵看看具體的做法:

_demoBGWorker.WorkerReportsProgress = true;
_demoBGWorker.ProgressChanged += BGWorker_ProgressChanged;

首先要把WorkerReportsProgress 屬性設置為 true,然後為ProgressChanged 事件添加處理方法:

private void BGWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    //修改進度條的顯示。
    this.progressBarSum.Value = e.ProgressPercentage;

    //如果有更多的信息需要傳遞,可以使用 e.UserState 傳遞一個自定義的類型。
    //這是一個 object 類型的對象,您可以通過它傳遞任何類型。
    //我們僅把當前 sum 的值通過 e.UserState 傳回,並通過顯示在視窗上。
    string message = e.UserState.ToString();
    this.labelSum.Text = message;
}

繼續更新 BGWorker_DoWork方法:

private void BGWorker_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker bgWorker = sender as BackgroundWorker;
    int endNumber = 0;
    if(e.Argument != null)
    {
        endNumber = (int)e.Argument;
    }

    int sum = 0;
    for (int i = 0; i <= endNumber; i++)
    {
        sum += i;
        
        string message = "Current sum is: " + sum.ToString();
        //ReportProgress 方法把信息傳遞給 ProcessChanged 事件處理函數。
        //第一個參數類型為 int,表示執行進度。
        //如果有更多的信息需要傳遞,可以使用 ReportProgress 的第二個參數。
        //這裡我們給第二個參數傳進去一條消息。
        bgWorker.ReportProgress(i, message);
        Thread.Sleep(600);
    }
}

OK,現在已經可以看到進度條和執行信息的更新了。

取消操作

在執行過程中允許用戶取消當前的操作是一個基本的設計,BackgroundWorker自然有很好的支持:

_demoBGWorker.WorkerSupportsCancellation = true;

和WorkerReportsProgress屬性一樣,如果要支持取消操作我們需要設置 WorkerSupportsCancellation屬性為 true。並且還要在BGWorker_DoWork方法中進行支持,在 for 迴圈中 Thread.Sleep(600)後面添加代碼:

bgWorker.ReportProgress(i, message);
Thread.Sleep(600);

//在操作的過程中需要檢查用戶是否取消了當前的操作。
if (bgWorker.CancellationPending == true)
{
    e.Cancel = true;
    break;
}

如果檢測到用戶點擊的取消按鈕,就退出當前的計算過程。下麵是點擊取消按鈕時要調用的代碼:

_demoBGWorker.CancelAsync();

現在已經可以支持取消操作了,趕快試試吧!

異常處理

如果在計算過程中發生了異常該怎麼處理?有沒有辦法知道計算過程已經結束?當然要有,即便是正常的結束也需要拿到計算的結果。

_demoBGWorker.RunWorkerCompleted += BGWorker_RunWorkerCompleted;
private void BGWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    //如果用戶取消了當前操作就關閉視窗。
    if (e.Cancelled)
    {
        this.Close();
    }

    //計算已經結束,需要禁用取消按鈕。
    this.btnCancel.Enabled = false;

    //計算過程中的異常會被抓住,在這裡可以進行處理。
    if (e.Error != null)
    {
        Type errorType = e.Error.GetType();
        switch (errorType.Name)
        {
            case "ArgumentNullException":
            case "MyException":
                //do something.
                break;
            default:
                //do something.
                break;
        }
    }

    //計算結果信息:e.Result
    //use it do something.
}

RunWorkerCompleted 事件處理函數會在DoWork 事件處理函數返回後被調用。通過它我們可以進行一些運算結束後的操作,比如禁用取消按鈕,異常處理,結果顯示等。
註意,如果想要拿到 e.Result,您需要在BGWorker_DoWork方法中設置 e.Result屬性,如:

e.Result = sum;

 

總結,BackgroundWorker 類功能完善且使用簡便,實在是處理非同步耗時操作的利器!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 創建新庫出錯如下: 主要原因是沒有許可權 給它 local system 許可權,問題解決 ...
  • 用戶用瀏覽器訪問一個網站,由於採用的http的特性,Web伺服器並不能知道是哪一個用戶正在訪問,但一些網站,希望能夠知道訪問者的一些信息,例如是不是第一次訪問,訪問者上次訪問時是否有未做完的工作,這次是否為其繼續工作提供方便等等。用瀏覽器訪問一個網站,可以在此網站的網頁之間跳轉,當從第一個網頁轉到第 ...
  • Session 每臺電腦訪問伺服器,都有獨立的session,key值都一樣,內容不一樣。 1.session保存在伺服器上。 2.session沒有持久性,保存周期就是20分鐘。 重點: session不要濫用,不要不用,濫用會造成伺服器溢出,不用會造成資源浪費。 賦值:Session["key" ...
  • Session: 與Cookies相比 相同點:每一臺電腦訪問伺服器,都會是獨立的一套session,key值都一樣,但是內容都是不一樣的 以上所有內容,都跟cookies一樣 不同點: 1、Session的保存位置是保存在伺服器上2、Session沒有持久的,它的保存周期就是20分鐘 重點:Ses ...
  • 內置對象: 1、Session:跟Cookies一樣用來存儲用戶數據,但保存位置不同,保存在伺服器記憶體上 每一臺電腦訪問伺服器,都會是獨立的一套session,key值都一樣,但是內容都是不一樣的 Session沒有持久的,保存20min 重點:Session不要濫用,也不要不用,濫用可能會造成服務 ...
  • 3.打開文件視圖 4.複製release目錄下的文件到Application Folder 5.創建兩個快捷方式,一個放到DeskTop,另一個放到Program Menu. 粘貼快捷方式到DeskTop和Program Menu 6.添加卸載功能: 找到C:\Windows\System32\Ms ...
  • 原文鏈接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository-pattern-and-dep/ 系列目錄: Relationship in Entity Framewo ...
  • 最近使用Castle.ActiveRecord框架,網上關於多數據支持的文章很少,因此有了這篇博文的產生。 開發工具VS2015,Sql Server2008R2 新建資料庫,數據初始化腳本如下: 3.新建MVC項目ActiveRecord.Demo,新建類庫Castle.ActiveRecord. ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...