4

在Winform开发中,使用Async-Awati异步任务处理代替BackgroundWorker

 2 years ago
source link: https://www.cnblogs.com/wuhuacong/p/16374158.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

在Winform开发中有时候我们为了不影响主UI线程的处理,以前我们使用后台线程BackgroundWorker来处理一些任务操作,不过随着异步处理提供的便利性,我们可以使用Async-Awati异步任务处理替换原来的后台线程BackgroundWorker处理方式,更加的简洁明了。

在一些耗时的操作过程中,在长时间运行时可能会导致用户界面 (UI) 处于停止响应状态,因此使用使用Async-Awati异步任务处理或者后台线程BackgroundWorker来处理一些任务操作很有必要。

在使用BackgroundWorker的过程中,我们可以定义自己的状态参数信息,从而实现线程状态的实时跟踪以及进度和信息提示,方便我们及时通知UI进行更新。

现在使用Async-Awati异步任务处理,一样可以在处理过程中通知UI更新进度和提示信息。

1、回顾BackgroundWorker后台线程的处理代码

我们先来了解一下BackgroundWorker后台线程的操作代码,对比下再介绍使用Async-Awati异步任务处理和通知操作。

一般的使用代码是需要初始化后台线程对象的,如下代码所示。

    public partial class MainFrame : BaseForm
    {
        /// <summary>
        /// 增加一个变量来记录线程状态
        /// </summary>
        private bool IsThreadRunning = false;
        private BackgroundWorker worker = new BackgroundWorker();

        public MainFrame()
        {
            InitializeComponent();

            Portal.gc.InitData();

            worker.WorkerSupportsCancellation = true;   //支持取消
            worker.WorkerReportsProgress = true;        //支持报告进度
            worker.DoWork += worker_DoWork;             //处理过程
            worker.RunWorkerCompleted += worker_RunWorkerCompleted; //完成操作
            worker.ProgressChanged += worker_ProgressChanged;       //报告进度
        }

例如进度条的通知,主要就是计算总任务的数量,并用于显示当前的任务进度信息,实例代码如下所示

        /// <summary>
        /// 进度条的通知
        /// </summary>
        void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.barProgress.EditValue = e.ProgressPercentage;
            CollectStateInfo stateInfo = e.UserState as CollectStateInfo;
            if (stateInfo != null)
            {
                var message = string.Format("正在采集 {0} 的 {1} , 项目名称为:{2}", stateInfo.TotalRecords, stateInfo.CompletedRecord + 1, stateInfo.CurrentItemName);
                this.lblTips.Text = message;
                this.barTips.Caption = message;

                //记录运行位置
                JobParameterHelper.SaveData(new CurrentJobParameter(stateInfo));
            }
        }

后台进程处理的关键事件就是处理过程的代码实现,它处理任务的时候,把当前的状态通过事件方式通知UI显示。

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
            Random r = new Random();
            int numCount = 0;
            while (worker.CancellationPending == false)
            {
                int num = r.Next(0, 10000);
                if (num % 5 == 0)
                {
                    numCount++;
                    worker.ReportProgress(0, num);
                    Thread.Sleep(1000);
                }
            }
            e.Result = numCount;
        }

触发任务开始的时候,我们调用代码如下所示。

    if (!worker.IsBusy)
    {
        worker.RunWorkerAsync(stateInfo);
    }

任务完成后,通知更新界面即可。

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    //还原按钮状态
    InitCollectState();
    IsThreadRunning = false;

    string message = "采集操作完成";
    MessageDxUtil.ShowTips(message);
}

2、使用Async-Awati异步任务处理代替BackgroundWorker

为了测试使用Asyn-Await异步处理,我创建一个简单的Demo程序,用于测试其效果。

8867-20220614114933374-1277605009.png

 窗体里面放置一个按钮,触发按钮执行任务操作,并逐步提示进度条信息,完成后提示任务完成。

8867-20220614115255937-2070270363.png

 为了在异步处理中提示进度信息,我们引入了Progress 线程通知对象。

8867-20220614115520833-1466915074.png

定义一个线程通知的Progress对象,如下所示。这里的int也可以换为自定义的对象类,以方便承载更多的信息。

  var reporter = new Progress<int>(progressChanged);

其中progressChanged 是我们定义的一个通知UI显示进度的处理函数,如下所示。

        /// <summary>
        /// 报告进度
        /// </summary>
        /// <param name="percentage">当前进度</param>
        void progressChanged(int percentage)
        {
            this.progressBar1.EditValue = percentage;

            this.progressPanel.Caption = percentage == 100 ? "任务已完成": "任务正在处理";
            this.progressPanel.Description = String.Format("完成【{0}%】", percentage);
        }

接着我们定义一个处理任务的WorkStart方法,接收一个Progress对象,如下代码所示。

      var reporter = new Progress<int>(progressChanged);
      var result = await this.WorkStart(reporter);

为了简单样式异步调用,我们这里只是延迟了一下处理任务,实际处理的话,调用异步方法即可。

        /// <summary>
        /// 执行任务
        /// </summary>
        private async Task<CommonResult> WorkStart(IProgress<int> progress)
        {
            var result = new CommonResult();
            for(int i = 0; i < 100; i++)
            {
                await Task.Delay(100);
                progress.Report(i + 1);
            }
            result.Success = true;

            return result;
        }

我们可以看到,任务每次执行到一个节点,就会调用对象方法Report进行通知处理。

而任务完成后,我们简单的通知处理即可。整段代码如下所示。

    /// <summary>
    /// Async Await异步线程处理
    /// </summary>
    public partial class FrmAsyncAwaitDemo : DevExpress.XtraEditors.XtraForm
    {
        public FrmAsyncAwaitDemo()
        {
            InitializeComponent();


            this.progressBar1.Visible = false;
            this.progressPanel.Visible = false;
        }

        private async void btnStart_Click(object sender, EventArgs e)
        {
            this.btnStart.Enabled = false;
            this.progressBar1.Visible = true;
            this.progressPanel.Visible = true;

            var reporter = new Progress<int>(progressChanged);
            var result = await this.WorkStart(reporter);
            this.WorkCompleted(result);
        }


        /// <summary>
        /// 任务完成
        /// </summary>
        /// <param name="result">返回结果CommonResult</param>
        void WorkCompleted(CommonResult result)
        {
            if (result.Success)
            {
                //操作成功的处理
            }

            var alert = new AlertControl();
            alert.FormLocation = AlertFormLocation.TopRight;
            alert.AutoFormDelay = 2000;
            alert.Show(this, "任务提示", result.Success ? "任务处理完成,操作成功" : result.ErrorMessage);

            this.progressBar1.Visible = false;
            this.progressPanel.Visible = false;
            this.btnStart.Enabled = true;
        }

        /// <summary>
        /// 报告进度
        /// </summary>
        /// <param name="percentage">当前进度</param>
        void progressChanged(int percentage)
        {
            this.progressBar1.EditValue = percentage;

            this.progressPanel.Caption = percentage == 100 ? "任务已完成": "任务正在处理";
            this.progressPanel.Description = String.Format("完成【{0}%】", percentage);
        }

        /// <summary>
        /// 执行任务
        /// </summary>
        private async Task<CommonResult> WorkStart(IProgress<int> progress)
        {
            var result = new CommonResult();
            for(int i = 0; i < 100; i++)
            {
                await Task.Delay(100);
                progress.Report(i + 1);
            }
            result.Success = true;

            return result;
        }
    }

在我们实际的案例中,文件上传处理就使用了这种方式来通知UI线程,任务处理的代码如下所示。

8867-20220614120450534-1310666471.png

 因此使用Async-Awati异步任务处理代替BackgroundWorker,代码更加简便,而且使用 IProgress接口类来处理通知,也是非常方便的。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK