• 基于事件的异步模式——BackgroundWorker


    转自strangeman原文 基于事件的异步模式——BackgroundWorker

      实现异步处理的方法很多,经常用的有基于委托的方式,今天记录的是基于事件的异步模式。利用BackgroundWorker组件可以很轻松的实现异步处理,并且该组件还支持事件的取消、进度报告等功能。本文以计算两个数XY的和为例。

      通过反编译可以看到,这个组件内部也是通过异步委托实现的,报告进度、取消事件等运用了事件技术实现,而事件的本质其实就是委托。

    程序界面如下图,其中三个文本框分别为两个加数和处理结果,两个按钮为计算和取消,按钮下方为进度条。

    引入BackgroundWorker组件,为DoWorkProgressChangedRunWorkerCompleted三个事件指定方法。
    WorkerReportsProgress属性设为true,以支持报告进度。
    WorkerSupportsCancellation属性设置为true,以支持取消。

    public MainForm()
    {
    
        InitializeComponent();
        this.backgroundWorker1.DoWork += this.BackgroundWorker1DoWork;
        this.backgroundWorker1.ProgressChanged += this.BackgroundWorker1ProgressChanged;
        this.backgroundWorker1.RunWorkerCompleted += this.BackgroundWorker1RunWorkerCompleted;
        this.backgroundWorker1.WorkerReportsProgress = true;
        this.backgroundWorker1.WorkerSupportsCancellation = true;
        this.buttonCancel.Enabled = false;
    }

     

     

    3. 当点击计算按钮时,调用BackgroundWorkerRunWorkerAsync方法实现异步处理,该方法支持一个object类型的参数。这里用元组Tuple<int,int>传递XY的值。调用RunWorkerAsync方法,将触发DoWork事件。

    this.backgroundWorker1.RunWorkerAsync(new Tuple<int,int>(Convert.ToInt32(this.textBoxX.Text), Convert.ToInt32(this.textBoxY.Text)));

     通过反编译看到以下实现代码:

     

    public void RunWorkerAsync(object argument)
    {
    
        if (this.isRunning)
        {
    
            throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerAlreadyRunning"));
    
        }
    
        this.isRunning = true;
    
        this.cancellationPending = false;
    
        this.asyncOperation = AsyncOperationManager.CreateOperation(null);
    
        this.threadStart.BeginInvoke(argument, null, null);
    
    }

     

    可以看到其内部也是通过异步委托实现的,其中threadStart的定义:private delegate void WorkerThreadStartDelegate(object argument);就是一个委托类型。

     

    4. 当点击取消按钮时,调用BackgroundWorkerCancelAsync方法,这将改变BackgroundWorkerCancellationPending属性的值为ture。当执行OnWork方法时可以根据CancellationPending属性,判断用户是否要取消事件。

    this.backgroundWorker1.CancelAsync();  

     再看看其内部:

    public void CancelAsync()
    {
        if (!this.WorkerSupportsCancellation)
        {
            throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerDoesntSupportCancellation"));
        }
    
        this.cancellationPending = true;
    }
    
     

    如上面所说,改变只读属性CancellationPending的值为ture

    5. 这里在DoWork方法中
    根据CancellationPending属性,判断是否取消。若取消,应在方法结束之前将DoWorkEventArgsCancel属性设置为ture。这将用于RunWorkerCompleted事件中判断事件取消还是正常完成。
    通过ReportProgress方法报告进度, 调用该方法时传递了一个int类型的参数,该方法将触发ProgressChanged事件。

    void BackgroundWorker1DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
    {
        for (int i = 0; i < 10; i++) {
    
    System.Threading.Thread.Sleep(500);//模拟耗时操作
    
    if (backgroundWorker1.CancellationPending) {
        e.Cancel = true
        return;
    } else {
        backgroundWorker1.ReportProgress(i * 10);
    }
        }
        var t = e.Argument as Tuple<int,int>;
        e.Result = t.Item1 + t.Item2;
    }
    
     

     再看报告进度是怎么基于事件实现的:

     

    1.我们调用的ReportProgress方法
    
    public void ReportProgress(int percentProgress)
    {
        this.ReportProgress(percentProgress, null);
    }
    
    2.ReportProgress另一个重载,可以带一个自定义参数,方便传递其他内容,例如一般安装程序中的“正在复制文件”,“正在注册相关组件”提示信息等。
    
    public void ReportProgress(int percentProgress, object userState)
    {
        if (!this.WorkerReportsProgress)
        {
         throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerDoesntReportProgress"));
        }
        ProgressChangedEventArgs progressChangedEventArgs = new ProgressChangedEventArgs(percentProgress, userState);
        if (this.asyncOperation != null)
        {
        this.asyncOperation.Post(this.progressReporter, progressChangedEventArgs);
    
        return;
        }
    
        this.progressReporter(progressChangedEventArgs);
    
    }

    3.触发事件

    private void ProgressReporter(object arg)
    {
        this.OnProgressChanged((ProgressChangedEventArgs)arg);
    }

    6. ProgressChanged事件,会将控制权交给UI线程。在其实现方法中根据ProgressChangedEventArgsProgressPercentage属性获取进度值。

    void BackgroundWorker1ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
    {
        this.progressBar1.Value = e.ProgressPercentage;
    }

     

    7. OnWork退出后,将调用RunWorkerCompleted事件。
    根据RunWorkerCompletedEventArgsCancelled属性判断是否正常完成。
    根据RunWorkerCompletedEventArgsResult属性获取处理结果。

    void BackgroundWorker1RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
    
    {
    
        this.textBoxResult.Text = e.Cancelled ? "Canceled" : e.Result.ToString();
    
        this.buttonCancel.Enabled = false;
    
        this.buttonCalculate.Enabled = true;
    
        this.progressBar1.Value = 100;
    
    }

     

     

    8. 完整代码

    /*
     * 由SharpDevelop创建。
     * 用户: David Huang
     * 日期: 2015/9/8
     * 时间: 14:54
     */
    using System;
    using System.Windows.Forms;
    
    namespace BackgroundWorkerTest
    {
        /// <summary>
        /// Description of MainForm.
        /// </summary>
        public partial class MainForm : Form
        {
            public MainForm()
            {
                InitializeComponent();
                this.backgroundWorker1.DoWork += this.BackgroundWorker1DoWork;
                this.backgroundWorker1.ProgressChanged += this.BackgroundWorker1ProgressChanged;
                this.backgroundWorker1.RunWorkerCompleted += this.BackgroundWorker1RunWorkerCompleted;
                this.backgroundWorker1.WorkerReportsProgress = true;
                this.backgroundWorker1.WorkerSupportsCancellation = true;
                this.buttonCancel.Enabled = false;
            }
            void ButtonCalculateClick(object sender, EventArgs e)
            {
                this.buttonCalculate.Enabled = false;
                this.textBoxResult.Text = string.Empty;
                this.buttonCancel.Enabled = true;
                this.backgroundWorker1.RunWorkerAsync(new Tuple<int,int>(Convert.ToInt32(this.textBoxX.Text), Convert.ToInt32(this.textBoxY.Text)));            
            }
            void ButtonCancelClick(object sender, EventArgs e)
            {
                this.backgroundWorker1.CancelAsync();            
            }
            void BackgroundWorker1DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
            {
                for (int i = 0; i < 10; i++) {
                    System.Threading.Thread.Sleep(500);        
                    if (backgroundWorker1.CancellationPending) {
                        e.Cancel = true;
                        return;
                    } else {
                        backgroundWorker1.ReportProgress(i * 10);
                    }
                }
                var t = e.Argument as Tuple<int,int>;
                e.Result = t.Item1 + t.Item2;
            }
            void BackgroundWorker1ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
            {
                this.progressBar1.Value = e.ProgressPercentage;
            }
            void BackgroundWorker1RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
            {
                this.textBoxResult.Text = e.Cancelled ? "Canceled" : e.Result.ToString();
                this.buttonCancel.Enabled = false;
                this.buttonCalculate.Enabled = true;
                this.progressBar1.Value = 100;
            }
        }
    }
    View Code


    代码下载:点我

  • 相关阅读:
    Nodejs 接收RabbitMQ消息
    c#后台线程更新界面
    Nodejs JSON.parse()无法解析ObjectID和ISODate的问题
    【百度小程序】细数百度小程序踩的坑
    工作中可能用到的工具
    input输入文字的时候背景会变色,如何去掉呢?
    百度小程序开发工具不能预览
    引入外部 CDN失效时--怎么加载本地资源文件(本文以jquery为例)
    swiper插件遇到的坑
    原生JS实现JQuery的addClass和removeClass
  • 原文地址:https://www.cnblogs.com/arxive/p/6697608.html
Copyright © 2020-2023  润新知