zoukankan      html  css  js  c++  java
  • BackgroundWorker简单实用(简便的异步操作)

      

    微软提供了一个快捷使用多线程的帮助类BackgroundWorker,能够快速创建一个新的线程,并能报告进度,暂停,以及在线程完成后处理别的任务。

    1.BackgroundWorker类介绍

         1.1. 四个常用属性:

          public bool IsBusy { get; }    //只读属性,用来判断当前线程是否正在工作中

        public bool WorkerReportsProgress { get; set; }   //决定当前线程是否能报告进度

        public bool WorkerSupportsCancellation { get; set; }   //决定当前线程能否取消

               public bool CancellationPending { get; }    //只读属性,用来判断是否发送了取消线程的消息(当调用CancelAsync()方法时,被设置为true)

         1.2. 三个常用事件:

        public event DoWorkEventHandler DoWork; //开始 必须,线程的主要逻辑,调用RunWorkerAsync()时触发该事件

        public event ProgressChangedEventHandler ProgressChanged; //报告 可选,报告进度事件,调用ReportProgress()时触发该事件

        public event RunWorkerCompletedEventHandler RunWorkerCompleted; //结束 可选,当线程运行完毕、发生异常和调用CancelAsync()方法这三种方式都会触发该事件

          1.3. 三个常用方法:

        public void RunWorkerAsync(); //启动线程,触发DoWork事件
        public void RunWorkerAsync(object argument);

        public void ReportProgress(int percentProgress); //报告进度,触发ProgressChanged事件
        public void ReportProgress(int percentProgress, object userState);

        public void CancelAsync(); //取消线程,将CancellationPending设置为true

    2.BackgroundWorker用法

           2.1. 简单用法:

                 新建BackgroundWorder对象;

                 根据需求, 设置是否能取消(WorkerSupportsCancellation)、是否报告进度(WorkerReportsProgress);

                 根据需求,设置好相关事件,DoWorker、ProgressChanged、ProgressChanged;

                 调用RunWorkerAsyns()方法,启动线程;

                 在需要取消的位置,判断CancellationPending的值,并做相关处理;//可选

                 在适当的位置调用ReportProgress(int percentProgress)方法,报告进度。

            2.2.简单例子:

                   2.2.1. 最基本的运行代码

                     

                     界面的简单代码:

                            界面上就一个Grid控件和2个Button

     1 <Window x:Class="BackgroundWorkerDemo20170324.MainWindow"
     2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     4         Title="MainWindow" Height="342" Width="504">
     5     <Grid>
     6         <ProgressBar x:Name="pBar" Margin="50,69,43,209">
     7         </ProgressBar>
     8         <Button x:Name="btnStart" Content="start" Click="btnStart_Click" Margin="50,218,311,63"/>
     9         <Button x:Name="btnCancel" Content="cancel" Click="btnCancel_Click" Margin="285,218,95,63"/>
    10     </Grid>
    11 </Window>
    View Code

                     后台代码:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.ComponentModel;
     4 using System.Linq;
     5 using System.Text;
     6 using System.Threading;
     7 using System.Windows;
     8 using System.Windows.Controls;
     9 using System.Windows.Data;
    10 using System.Windows.Documents;
    11 using System.Windows.Input;
    12 using System.Windows.Media;
    13 using System.Windows.Media.Imaging;
    14 using System.Windows.Navigation;
    15 using System.Windows.Shapes;
    16 
    17 namespace BackgroundWorkerDemo20170324
    18 {
    19     /// <summary>
    20     /// Interaction logic for MainWindow.xaml
    21     /// </summary>
    22     public partial class MainWindow : Window
    23     {
    24         private BackgroundWorker worker;
    25         public MainWindow()
    26         {
    27             InitializeComponent();
    28             worker = new BackgroundWorker();                      //新建BackgroundWorker
    29             worker.WorkerReportsProgress = true;                  //允许报告进度
    30             worker.WorkerSupportsCancellation = true;             //允许取消线程
    31             worker.DoWork += worker_DoWork;                       //设置主要工作逻辑
    32             worker.ProgressChanged += worker_ProgressChanged;     //进度变化的相关处理
    33             worker.RunWorkerCompleted += worker_RunWorkerCompleted;  //线程完成时的处理
    34         }
    35 
    36         /// <summary>
    37         /// 主要工作逻辑
    38         /// </summary>
    39         /// <param name="sender"></param>
    40         /// <param name="e"></param>
    41         private void worker_DoWork(object sender, DoWorkEventArgs e)
    42         {
    43             BackgroundWorker tempWorker = sender as BackgroundWorker;
    44             for (int i = 0; i <= 100; i++)
    45             {
    46                 Thread.Sleep(200);    //避免太快,让线程暂停一会再报告进度
    47                 tempWorker.ReportProgress(i);   //调用ReportProgress()方法报告进度,同时触发ProgressChanged事件
    48             }
    49         }
    50 
    51         /// <summary>
    52         /// 处理进度变化,改变进度条的值
    53         /// </summary>
    54         /// <param name="sender"></param>
    55         /// <param name="e"></param>
    56         private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    57         {
    58             pBar.Value = e.ProgressPercentage;
    59         }
    60 
    61         /// <summary>
    62         /// 线程完成后的处理
    63         /// </summary>
    64         /// <param name="sender"></param>
    65         /// <param name="e"></param>
    66         private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    67         {
    68             MessageBox.Show("线程工作完成");
    69         }
    70 
    71         /// <summary>
    72         /// 点击Start按钮启动线程
    73         /// </summary>
    74         /// <param name="sender"></param>
    75         /// <param name="e"></param>
    76         private void btnStart_Click(object sender, RoutedEventArgs e)
    77         {
    78             worker.RunWorkerAsync();   //调用该方法才会启动线程
    79         }
    80 
    81         /// <summary>
    82         /// 点击Cancel按钮取消线程,但先判断线程是否正在工作
    83         /// </summary>
    84         /// <param name="sender"></param>
    85         /// <param name="e"></param>
    86         private void btnCancel_Click(object sender, RoutedEventArgs e)
    87         {
    88             if (worker.IsBusy)
    89                 worker.CancelAsync();
    90             else
    91                 MessageBox.Show("There is no thead running now.");
    92         }
    93     }
    94 }
    View Code

             2.2.2. 能取消线程

                   在需要取消线程的位置判断CancellationPending属性,一般在循环体中(因为循环一般耗时居多),判断当CancellationPending==true时,

      需要将DoWorkEventArgs的Cancel属性设置为true, 然后就可以在RunWorkerCompleted中判断RunWorkerCompletedEventArgs的Cancelled

      属性来进行对应的处理(即,当用户取消线程时,也会触发RunWorkerCompleted事件)

              修改后的代码:

      1 using System;
      2 using System.Collections.Generic;
      3 using System.ComponentModel;
      4 using System.Linq;
      5 using System.Text;
      6 using System.Threading;
      7 using System.Windows;
      8 using System.Windows.Controls;
      9 using System.Windows.Data;
     10 using System.Windows.Documents;
     11 using System.Windows.Input;
     12 using System.Windows.Media;
     13 using System.Windows.Media.Imaging;
     14 using System.Windows.Navigation;
     15 using System.Windows.Shapes;
     16 
     17 namespace BackgroundWorkerDemo20170324
     18 {
     19     /// <summary>
     20     /// Interaction logic for MainWindow.xaml
     21     /// </summary>
     22     public partial class MainWindow : Window
     23     {
     24         private BackgroundWorker worker;
     25         public MainWindow()
     26         {
     27             InitializeComponent();
     28             worker = new BackgroundWorker();                      //新建BackgroundWorker
     29             worker.WorkerReportsProgress = true;                  //允许报告进度
     30             worker.WorkerSupportsCancellation = true;             //允许取消线程
     31             worker.DoWork += worker_DoWork;                       //设置主要工作逻辑
     32             worker.ProgressChanged += worker_ProgressChanged;     //进度变化的相关处理
     33             worker.RunWorkerCompleted += worker_RunWorkerCompleted;  //线程完成时的处理
     34         }
     35 
     36         /// <summary>
     37         /// 主要工作逻辑
     38         /// </summary>
     39         /// <param name="sender"></param>
     40         /// <param name="e"></param>
     41         private void worker_DoWork(object sender, DoWorkEventArgs e)
     42         {
     43             BackgroundWorker tempWorker = sender as BackgroundWorker;
     44             for (int i = 0; i <= 100; i++)
     45             {
     46                 if (tempWorker.CancellationPending)  //当点击Cancel按钮时,CancellationPending被设置为true
     47                 {
     48                     e.Cancel = true;  //此处设置Cancel=true后,就可以在RunWorkerCompleted中判断e.Cancelled是否为true
     49                     break;
     50                 }
     51                 Thread.Sleep(200);    //避免太快,让线程暂停一会再报告进度
     52                 tempWorker.ReportProgress(i);   //调用ReportProgress()方法报告进度,同时触发ProgressChanged事件
     53             }
     54         }
     55 
     56         /// <summary>
     57         /// 处理进度变化,改变进度条的值
     58         /// </summary>
     59         /// <param name="sender"></param>
     60         /// <param name="e"></param>
     61         private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
     62         {
     63             pBar.Value = e.ProgressPercentage;
     64         }
     65 
     66         /// <summary>
     67         /// 线程完成后的处理
     68         /// </summary>
     69         /// <param name="sender"></param>
     70         /// <param name="e"></param>
     71         private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
     72         {
     73             if (e.Cancelled)  //被取消时
     74                 MessageBox.Show("线程被取消了");
     75             else              //正常结束   
     76                 MessageBox.Show("线程工作完成");
     77         }
     78 
     79         /// <summary>
     80         /// 点击Start按钮启动线程
     81         /// </summary>
     82         /// <param name="sender"></param>
     83         /// <param name="e"></param>
     84         private void btnStart_Click(object sender, RoutedEventArgs e)
     85         {
     86             worker.RunWorkerAsync();   //调用该方法才会启动线程
     87         }
     88 
     89         /// <summary>
     90         /// 点击Cancel按钮取消线程,但先判断线程是否正在工作
     91         /// </summary>
     92         /// <param name="sender"></param>
     93         /// <param name="e"></param>
     94         private void btnCancel_Click(object sender, RoutedEventArgs e)
     95         {
     96             if (worker.IsBusy)
     97                 worker.CancelAsync();
     98             else
     99                 MessageBox.Show("There is no thead running now.");
    100         }
    101     }
    102 }
    View Code

    3.线程中的传值

          3.1. 与BackgroundWorker相关的三个参数类

        //
            // 摘要: 
            //     引发 System.ComponentModel.BackgroundWorker.DoWork 事件。
            //
            // 参数: 
            //   e:
            //     包含事件数据的 System.EventArgs。
            protected virtual void OnDoWork(DoWorkEventArgs e);
            //
            // 摘要: 
            //     引发 System.ComponentModel.BackgroundWorker.ProgressChanged 事件。
            //
            // 参数: 
            //   e:
            //     包含事件数据的 System.EventArgs。
            protected virtual void OnProgressChanged(ProgressChangedEventArgs e);
            //
            // 摘要: 
            //     引发 System.ComponentModel.BackgroundWorker.RunWorkerCompleted 事件。
            //
            // 参数: 
            //   e:
            //     包含事件数据的 System.EventArgs。
            protected virtual void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e);

            通过上面的代码我们可以看到,BackgroundWorker的三个常用事件都有与之对应的参数类:

                 3.1.1. DoWorkEventArgs 为DoWork事件提供数据,详细代码如下:

     1 #region 程序集 System.dll, v2.0.0.0
     2 // C:WindowsMicrosoft.NETFrameworkv2.0.50727System.dll
     3 #endregion
     4 
     5 using System;
     6 
     7 namespace System.ComponentModel
     8 {
     9     // 摘要: 
    10     //     为 System.ComponentModel.BackgroundWorker.DoWork 事件处理程序提供数据。
    11     public class DoWorkEventArgs : CancelEventArgs
    12     {
    13         // 摘要: 
    14         //     初始化 System.ComponentModel.DoWorkEventArgs 类的新实例。
    15         //
    16         // 参数: 
    17         //   argument:
    18         //     指定异步操作的参数。
    19         public DoWorkEventArgs(object argument);
    20 
    21         // 摘要: 
    22         //     获取表示异步操作参数的值。
    23         //
    24         // 返回结果: 
    25         //     表示异步操作参数的 System.Object。
    26         public object Argument { get; }
    27         //
    28         // 摘要: 
    29         //     获取或设置表示异步操作结果的值。
    30         //
    31         // 返回结果: 
    32         //     表示异步操作结果的 System.Object。
    33         public object Result { get; set; }
    34     }
    35 }
    View Code

         只有两个object类型的属性Argument(只读的)和Result,

              因为调用RunWorkerAsync()方法,就会触发DoWork事件,细心的你会发现,RunWorkerAsync()方法有两个重载,

              带有参数的RunWorkerAsync(object argument),这个形参argument就是传递给DoWorkEventArgs的Arument属性的

              而Result属性是作为DoWork事件的结果传递给RunWorkerCompletedEventArgs的Result属性,

              

                 3.1.2.  ProgressChangedEventArgs 为ProgressChanged事件提供数据,详细代码如下:

     1 using System;
     2 
     3 namespace System.ComponentModel
     4 {
     5     // 摘要: 
     6     //     为 System.ComponentModel.BackgroundWorker.ProgressChanged 事件提供数据。
     7     public class ProgressChangedEventArgs : EventArgs
     8     {
     9         // 摘要: 
    10         //     初始化 System.ComponentModel.ProgressChangedEventArgs 类的新实例。
    11         //
    12         // 参数: 
    13         //   progressPercentage:
    14         //     已完成的异步任务的百分比。
    15         //
    16         //   userState:
    17         //     唯一的用户状态。
    18         public ProgressChangedEventArgs(int progressPercentage, object userState);
    19 
    20         // 摘要: 
    21         //     获取异步任务的进度百分比。
    22         //
    23         // 返回结果: 
    24         //     指示异步任务进度的百分比值。
    25         public int ProgressPercentage { get; }
    26         //
    27         // 摘要: 
    28         //     获取唯一的用户状态。
    29         //
    30         // 返回结果: 
    31         //     指示用户状态的唯一 System.Object。
    32         public object UserState { get; }
    33     }
    34 }
    View Code

                 也只有两个属性,都是只读的,一个为int类型的ProgerssPercentage,表示任务进度百分百;另一个是object类型的UserState

                 这两个参数都是通过ReportProgress()方法传入,由于UserState属性时object类型的,所以当需要实现复制逻辑时,可以自定义一个类型

                 3.1.3. RunWorkerCompletedEventArgs 稍微特殊一点,虽然该类的直接定义中只有两个属性,

                object类型的两个只读属性Result和UserState

     1 namespace System.ComponentModel
     2 {
     3     public class RunWorkerCompletedEventArgs : AsyncCompletedEventArgs
     4     {
     5         public RunWorkerCompletedEventArgs(object result, Exception error, bool cancelled);
     6 
     7         public object Result { get; }
     8         [Browsable(false)]
     9         [EditorBrowsable(EditorBrowsableState.Never)]
    10         public object UserState { get; }
    11     }
    12 }
    View Code

                但从父类继承过来的Cancelled和Error属性才是重点

     1 namespace System.ComponentModel
     2 {
     3     public class AsyncCompletedEventArgs : EventArgs
     4     {
     5         public AsyncCompletedEventArgs(Exception error, bool cancelled, object userState);
     6 
     7         [SRDescription("Async_AsyncEventArgs_Cancelled")]
     8         public bool Cancelled { get; }
     9         [SRDescription("Async_AsyncEventArgs_Error")]
    10         public Exception Error { get; }
    11         [SRDescription("Async_AsyncEventArgs_UserState")]
    12         public object UserState { get; }
    13 
    14         protected void RaiseExceptionIfNecessary();
    15     }
    16 }
    View Code

               所以,RunWorkerCompletedEventArgs参数主要关心三个属性,ErrorCancelledResult (UserState一般很少用到)

               注意:标准的RunWorkerCompleted处理都应该先处理Error和Cancelled情况,否则直接访问Result时会报错,

           尽量以这种方式来整理自己的代码逻辑:

     1 // This event handler deals with the results of the
     2 // background operation.
     3 private void backgroundWorker1_RunWorkerCompleted(
     4     object sender, RunWorkerCompletedEventArgs e)
     5 {
     6     // First, handle the case where an exception was thrown.
     7     if (e.Error != null)
     8     {
     9         MessageBox.Show(e.Error.Message);
    10     }
    11     else if (e.Cancelled)
    12     {
    13         // Next, handle the case where the user canceled 
    14         // the operation.
    15         // Note that due to a race condition in 
    16         // the DoWork event handler, the Cancelled
    17         // flag may not have been set, even though
    18         // CancelAsync was called.
    19         resultLabel.Text = "Canceled";
    20     }
    21     else
    22     {
    23         // Finally, handle the case where the operation 
    24         // succeeded.
    25         resultLabel.Text = e.Result.ToString();
    26     }
    27 }
    View Code

    4.注意事项

          4.1. DoWork事件中不能与UI控件进行交流

                  如果需要在线程处理过程中与UI控件进行交流,请在ProgressChanged和RunWorkerCompleted中进行,否则会出现以下错误

    System.InvalidOperationException was unhandled by user code
      Message=The calling thread cannot access this object because a different thread owns it.
      Source=WindowsBase
      StackTrace:
           at System.Windows.Threading.Dispatcher.VerifyAccess()
           at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
           at System.Windows.Controls.Primitives.RangeBase.set_Value(Double value)
           at BackgroundWorkerDemo20170324.MainWindow.worker_DoWork(Object sender, DoWorkEventArgs e) in 

    5.附带

          5.1. Visual Studio中的API显示如下:

                 代码:

     1 using System;
     2 
     3 namespace System.ComponentModel
     4 {
     5     [DefaultEvent("DoWork")]
     6     public class BackgroundWorker : Component
     7     {
     8         public BackgroundWorker();
     9 
    10         [Browsable(false)]
    11         public bool CancellationPending { get; }                          //用于判断是否取消了线程
    12         [Browsable(false)]
    13         public bool IsBusy { get; }                                       //判断当前线程是否正在工作
    14         [DefaultValue(false)]
    15         public bool WorkerReportsProgress { get; set; }                   //是否报告进度
    16         [DefaultValue(false)]
    17         public bool WorkerSupportsCancellation { get; set; }              //是否支持取消线程,如果要调用CancelAsyns()方法,必须设置为true
    18 
    19         public event DoWorkEventHandler DoWork;                           //线程核心代码,耗时的操作放在这里,调用RunWorkerAsync()时触发            
    20         public event ProgressChangedEventHandler ProgressChanged;         //当进度改变后执行的代码,调用ReportProgress()时触发
    21         public event RunWorkerCompletedEventHandler RunWorkerCompleted;   //3种情况:当线程完成、手动取消线程时、线程发生异常时触发
    22 
    23         public void CancelAsync();                                        //调用该方法会发出取消线程的消息,但并不会立即中止线程
    24         protected virtual void OnDoWork(DoWorkEventArgs e);
    25         protected virtual void OnProgressChanged(ProgressChangedEventArgs e);
    26         protected virtual void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e);
    27         public void ReportProgress(int percentProgress);                  //调用该方法就触发ProgressChanged事件,相当于ReportProgress(int percentProgress, null)
    28         public void ReportProgress(int percentProgress, object userState); 
    29         public void RunWorkerAsync();                                     //调用该方法启动线程,同时触发DoWork事件,相当于RunWorkerAsync(null)
    30         public void RunWorkerAsync(object argument);
    31     }
    32 }
    View Code

     

         5.2. 相关博客推荐,非常优秀,值得查看:

                   [你必须知道的异步编程]——基于事件的异步编程模式 ;

                   http://m.blog.csdn.net/article/details?id=7291070 

  • 相关阅读:
    Intellij IDEA + Jrebel
    WebConfig配置详解大全
    .Net 获取前端传递的数据
    js 格式验证大全
    EasyUI DataGrid 时间格式化、字符串长度截取
    SQL取某个字符串最后一次出现的位置后面的字符串方法
    公民身份号码校验码算法(C#版)
    组织机构代码校验码生成算法(C#版)
    MySQL实现根据当前ID读取上一条和下一条记录
    js jquery.pagination.js分页
  • 原文地址:https://www.cnblogs.com/tommy-huang/p/6612187.html
Copyright © 2011-2022 走看看