知识问答

C# BackgroundWorker用法详解

我们来详细讲解一下C#中的BackgroundWorker用法。

一、BackgroundWorker 是什么?

在C#中,BackgroundWorker是一个多线程组件,用于在后台执行一个操作并在主界面上更新相应的进度。它避免了在主线程中直接执行操作而引起的冻结UI界面的问题。

二、BackgroundWorker 的声明

我们使用 BackgroundWorker,需要在代码中添加一个 BackgroundWorker 控件。在 Visual Studio 中,您可以使用工具箱中的控件添加 BackgroundWorker,也可以使用设计器将其添加到表单中。

using System.ComponentModel;namespace MyApplication{    public class MyClass    {        BackgroundWorker backgroundWorker;        public MyClass()        {            backgroundWorker = new BackgroundWorker();            backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);            backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);            backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);            backgroundWorker.WorkerReportsProgress = true;          }    }}

这个代码片段中,我们首先在类中声明了 BackgroundWorker 对象。我们还初始化了 backgroundWorker 对象并指定了三个事件处理程序。

  • DoWork: 当调用 RunWorkerAsync 方法时,这个事件处理程序将异步执行。
  • RunWorkerCompleted: 这个事件处理程序在异步操作完成并结束时调用。
  • ProgressChanged: 这个事件处理程序在异步操作执行并更改了进度报告时调用。

您还可以在构造函数中设置 WorkReportsProgress 属性为 true,以便在异步操作执行时能够报告进度。

三、BackgroundWorker 的常用方法和属性

BackgroundWorker 有以下常用方法和属性:

  • DoWorkEventArgs.Argument 属性:获取指定异步任务的参数。通过这个属性,我们可以向异步任务传递参数。
  • BackgroundWorker.RunWorkerAsync 方法:启动异步操作。
  • BackgroundWorker.CancellationPending 属性:获取异步操作是否应被取消的值。
  • BackgroundWorker.CancelAsync 方法:请求取消异步操作。
  • BackgroundWorker.ReportProgress 方法:报告异步操作的进度。

四、BackgroundWorker 的使用示例

示例1:异步操作,可取消

假设我们要执行一个耗时的操作,这里使用一个简单的循环。因为循环执行时间较长,我们需要在一个单独的线程中执行,以确保应用程序在执行操作时不会被冻结。

这里我们以一个计数器为例,每100ms加1,达到10时程序结束。

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e){    BackgroundWorker worker = sender as BackgroundWorker;    int total = (int)e.Argument;    int count = 0;    while (count < total)    {        if (worker.CancellationPending)        {            e.Cancel = true;            break;        }        Thread.Sleep(100);        count++;        int progress = (int)((double)count / total * 100);        worker.ReportProgress(progress, count);    }}private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e){    if (e.Cancelled)    {        MessageBox.Show("操作被取消");    }    else if (e.Error != null)    {        MessageBox.Show("操作出现错误: " + e.Error.Message);    }    else    {        MessageBox.Show("操作完成");    }}private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e){    progressBar.Value = e.ProgressPercentage;    label.Text = "计数:" + e.UserState.ToString();}private void btnStart_Click(object sender, EventArgs e){    int count = 10;    if (int.TryParse(textBox.Text, out count))    {        if (!backgroundWorker.IsBusy)        {            btnStart.Enabled = false;            btnCancel.Enabled = true;            progressBar.Value = 0;            label.Text = "计数:0";            backgroundWorker.RunWorkerAsync(count);        }    }    else    {        MessageBox.Show("请输入一个数字");    }}private void btnCancel_Click(object sender, EventArgs e){    if (backgroundWorker.IsBusy)    {        btnCancel.Enabled = false;        backgroundWorker.CancelAsync();    }}

在这个示例中,我们使用 DoWork 事件完成循环操作。当 BackgroundWorker 的 CancellationPending 属性设置为 true 时,我们可以结束操作。 ProgressChanged 事件可以显示操作的当前进度,因此您可以监视操作的进程。

RunWorkerCompleted 事件将会调用在后台线程上执行的异步操作执行完成时调用的事件处理程序。 在事件处理程序中,我们可以检查 IsCanceled 属性是否为 true,以判断操作是正常结束还是被取消。

示例2:使用异步操作和进度报告

下一个示例仍然是异步操作,但由于计算不同,我们需要一种不同的方法来计算进度。在这个示例中,我们计算质数。正在计算质数时,线程睡眠1ms,以便其他程序继续运行。该计算迭代10,000, 因此,我们需要对已完成的百分比进行测量。

void backgroundWorker_DoWork(object sender, DoWorkEventArgs e){    BackgroundWorker worker = sender as BackgroundWorker;    int count = 0;    for (int i = 2; i <= 10000; i++)    {        if (worker.CancellationPending)        {            e.Cancel = true;            break;        }        bool isPrime = true;        for (int j = 2; j < i; j++)        {            if (i % j == 0)            {                isPrime = false;                break;            }        }        if (isPrime)        {            count++;            double percentage = (double)count / (double)10000 * 100;            worker.ReportProgress((int)Math.Floor(percentage), count);        }        Thread.Sleep(1);    }}void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e){    progressBar.Value = e.ProgressPercentage;    labelPercentage.Text = e.ProgressPercentage.ToString() + "%";    labelCount.Text = e.UserState.ToString();}void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e){    if (e.Cancelled)    {        MessageBox.Show("质数计算被取消。");    }    else if (e.Error != null)    {        MessageBox.Show("质数计算异常: " + e.Error.Message);    }    else    {        labelCount.Text = "质数总共为:" + e.Result.ToString();        MessageBox.Show("质数计算已完成!");    }}void buttonStart_Click(object sender, EventArgs e){    buttonStart.Enabled = false;    buttonCancel.Enabled = true;    progressBar.Value = 0;    labelPercentage.Text = "0%";    labelCount.Text = "总数为:0";    backgroundWorker.RunWorkerAsync();}void buttonCancel_Click(object sender, EventArgs e){    if (backgroundWorker.IsBusy)    {        backgroundWorker.CancelAsync();    }}

在这个示例中,使用异步操作计算质数,并使用一个进度条显示操作的进度。当 BackgroundWorker 的 ReportProgress 方法被调用时,ProgressChanged 事件就会触发, 其中, ReportProgress 用来实现异步报告进度,可以带有一个当前进度百分比,和一个可选的用户状态对象,以显示进度。 运行完成后,我们会调用 RunWorkerCompleted 事件处理程序,它在异步操作运行并完成时调用。在事件处理程序中,我们可以检查 IsCanceled 属性是否为 true,以判断操作是正常结束还是被取消。