首頁  >  文章  >  後端開發  >  C#中BackgroundWorker用法的詳解(圖)

C#中BackgroundWorker用法的詳解(圖)

黄舟
黄舟原創
2017-04-13 09:58:112456瀏覽

這篇文章主要介紹了C# BackgroundWorker使用詳解 ,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟著小編過來看看吧

在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 類別功能完善且使用簡單,實在是處理非同步耗時操作的利器!

以上是C#中BackgroundWorker用法的詳解(圖)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn