首页 > 技术文章 > C# 异步调用中 BackgroundWorker的使用

qtiger 2020-08-13 11:23 原文

一、概述   

  • BackgroundWorker 类允许您在单独的专用线程上运行操作,在界面上报告进度,接受界面的控制信号,返回运算结果。

  • BackgroundWorker 的DoWork代码运行在非UI线程之上,BackgroundWorker 不跨 AppDomain 边界进行封送处理。

  • BackgroundWorker 是基于事件的异步编程模式EAP的复杂实现。

二、BackgroundWorker 配置

1、初始化BackgroundWorker 

主要需要对BackgroundWorker 绑定一下三个事件:  

  • 开始工作的事件:DoWork——非UI线程执行,不可操作界面控件对象
  • 完成工作的事件:RunWorkerCompleted——UI线程执行
  • 工作中刷新的事件:ProgressChanged——UI线程执行
     public static void BackgroundWorker1_Init()
        {
            BackgroundWorker backgroundWorker1 = new BackgroundWorker();
            backgroundWorker1.WorkerReportsProgress = true;//能否报告进度更新。
            backgroundWorker1.WorkerSupportsCancellation = true;//是否支持异步取消
            //绑定事件
            backgroundWorker1.DoWork += new DoWorkEventHandler(BackgroundWorker1_DoWork);
            backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(BackgroundWorker1_ProgressChanged);
            backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorker1_RunWorkerCompleted);

            //启动BackgroundWorker
            if (backgroundWorker1.IsBusy != true)//判断BackgroundWorker 是否正在运行异步操作。
            {
                backgroundWorker1.RunWorkerAsync("object argument");//启动异步操作,有两种重载(有参和无参),将触发BackgroundWorker.DoWork事件
            }
        }

2、DoWork开始事件

     /// <summary>
        /// 句柄sender指向的就是该BackgroundWorker。
        /// e.Argument 获取异步操作参数的值  
        /// e.Cancel 是否应该取消事件
        /// e.Result  获取或设置异步操作结果的值(在RunWorkerCompleted事件可能会使用到)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            object value = e.Argument;//获取RunWorkerAsync(object argument)传入的值
            BackgroundWorker worker = sender as BackgroundWorker;

            for (int i = 1; i <= 10; i++)
            {
                if (worker.CancellationPending == true)//在耗时操作中判断CancellationPending属性,如果为true则退出
                {
                    e.Cancel = true;
                    break;
                }
                else
                {
                    // 执行耗时操作
                    System.Threading.Thread.Sleep(1000);
                    worker.ReportProgress(i * 10, "Object userState");// 将触发BackgroundWorker.ProgressChanged事件,向ProgressChanged报告进度
                }
            }
            e.Result = "结束";
        }

3、RunWorkerCompleted完成事件

当DoWork事件处理完成之后,将会触发该事件处理工作结果。事件可根据传入的参数e来区分工作是中止或完成。

     /// <summary>
        /// e.Cancelled指示异步操作是否已被取消
        /// e.Error 指示异步操作期间发生的错误
        /// e.Result 获取异步操作结果的值,即DoWork事件中,Result设置的值。    
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void BackgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //判断是否用户手动取消,若程序要支持此处功能,需要程序中有cancel的动作,并在该动作中将e.cancel置为true
            if (e.Cancelled == true)
            {
                //添加用户手动取消的动作,并在标签控件中进行提示  
                Console.WriteLine($"操作已经被取消!");
            }
            //判断是否由错误造成意外中止
            else if (e.Error != null)
            {
                //若发生错误,在标签控件中显示错误信息
                Console.WriteLine($"操作发生错误!");
            }
            //判断是否正常结束
            else
            {
                //添加正常结束之后的收尾动作,并在标签控件中进行提示
                Console.WriteLine($"执行结果:{e.Result.ToString()}!");
            }
        }

4、ProgressChanged刷新事件

  更新进度条的进度,此处也可以加入用户在过程中需要实时刷新的内容

     /// <summary>
        /// 进度刷新
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void BackgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            //接收ReportProgress方法传递过来的userState
            string state = (string)e.UserState;
            //e.ProgressPercentage  获取异步操作进度的百分比
            Console.WriteLine($"进度:{e.ProgressPercentage.ToString() + "%"};状态:{state}");
        }

5、取消BackgroundWorker

取消BackgroundWorker 需要在DoWork中循环检查BackgroundWorker.CancellationPending是否为true。

//终止
private void  BackgroundWorker1_End()
{
    if (backgroundWorker1.WorkerSupportsCancellation == true)
    {
        // Cancel the asynchronous operation.
        backgroundWorker1.CancelAsync(); //请求取消挂起的后台操作。调用该方法将使BackgroundWorker.CancellationPending属性设置为True。 
    }
}
 

三、完整的代码

using System;
using System.ComponentModel;

namespace BackgroundWorkerDemo
{
    public class BackgroundWorkerInit
    {
        public static void BackgroundWorker1_Init()
        {
            BackgroundWorker backgroundWorker1 = new BackgroundWorker();
            backgroundWorker1.WorkerReportsProgress = true;//能否报告进度更新。
            backgroundWorker1.WorkerSupportsCancellation = true;//是否支持异步取消
            //绑定事件
            backgroundWorker1.DoWork += new DoWorkEventHandler(BackgroundWorker1_DoWork);
            backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(BackgroundWorker1_ProgressChanged);
            backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorker1_RunWorkerCompleted);

            //启动BackgroundWorker
            if (backgroundWorker1.IsBusy != true)//判断BackgroundWorker 是否正在运行异步操作。
            {
                backgroundWorker1.RunWorkerAsync("object argument");//启动异步操作,有两种重载(有参和无参),将触发BackgroundWorker.DoWork事件
            }
        }

        /// <summary>
        /// 句柄sender指向的就是该BackgroundWorker。
        /// e.Argument 获取异步操作参数的值  
        /// e.Cancel 是否应该取消事件
        /// e.Result  获取或设置异步操作结果的值(在RunWorkerCompleted事件可能会使用到)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            object value = e.Argument;//获取RunWorkerAsync(object argument)传入的值
            BackgroundWorker worker = sender as BackgroundWorker;

            for (int i = 1; i <= 10; i++)
            {
                if (worker.CancellationPending == true)//在耗时操作中判断CancellationPending属性,如果为true则退出
                {
                    e.Cancel = true;
                    break;
                }
                else
                {
                    // 执行耗时操作
                    System.Threading.Thread.Sleep(1000);
                    worker.ReportProgress(i * 10, "Object userState");// 将触发BackgroundWorker.ProgressChanged事件,向ProgressChanged报告进度
                }
            }
            e.Result = "结束";
        }


        /// <summary>
        /// e.Cancelled指示异步操作是否已被取消
        /// e.Error 指示异步操作期间发生的错误
        /// e.Result 获取异步操作结果的值,即DoWork事件中,Result设置的值。    
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void BackgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //判断是否用户手动取消,若程序要支持此处功能,需要程序中有cancel的动作,并在该动作中将e.cancel置为true
            if (e.Cancelled == true)
            {
                //添加用户手动取消的动作,并在标签控件中进行提示  
                Console.WriteLine($"操作已经被取消!");
            }
            //判断是否由错误造成意外中止
            else if (e.Error != null)
            {
                //若发生错误,在标签控件中显示错误信息
                Console.WriteLine($"操作发生错误!");
            }
            //判断是否正常结束
            else
            {
                //添加正常结束之后的收尾动作,并在标签控件中进行提示
                Console.WriteLine($"执行结果:{e.Result.ToString()}!");
            }
        }


        /// <summary>
        /// 进度刷新
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private static void BackgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            //接收ReportProgress方法传递过来的userState
            string state = (string)e.UserState;
            //e.ProgressPercentage  获取异步操作进度的百分比
            Console.WriteLine($"进度:{e.ProgressPercentage.ToString() + "%"};状态:{state}");
        }
    }
}
using System;

namespace BackgroundWorkerDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            BackgroundWorkerInit.BackgroundWorker1_Init();
            Console.ReadKey();
        }
    }
}

执行结果:

 

推荐阅读