异步委托方式取消BackGroundWorker执行无循环的耗时方法

边学习边分享,纯属抛砖引玉。

线程的一个好处是异步的执行操作,在winform中,很多耗时操作执行时,为优化用户体验,避免长时间等待,从而运用线程技术异步的执行耗时操作,但不会阻塞主线程。

最近系统很多耗时查询导致体验很差,于是想到了用BackGroundWorker异步处理。而且要支持某些耗时达到几十秒的操作,可以取消。

BackGroundWorker有CancelAsync()这个方法。该方法只会将BackGroundWorker的CancellationPending属性设为true,但不会实际终止线程!网上找了些资料,代码如下

  private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {

        for (int i = 0; i < 100; i++)
                    {
                     if (backgroundWorker1.CancellationPending)  {
                                    e.Cancel = true;
                                    return;
                                }
                    }
}            

即在doWork事件中,不停的循环判断是否执行了取消操作。这种适用于执行多次方法时,而且无法在某个方法执行时将线程取消。如果我doWork事件里,没有for循环,而是一次耗时的数据访问操作,那这样的处理没有任何意义。

于是在下想到,可以在doWork事件里做如下处理

1.异步的去执行耗时操作。

2.while监视异步耗时操作是否完成,只要未完成就一直循环。循环内部判断是否执行了取消操作。如果执行了取消操作,则终止线程。代码如下

       private delegate int LongTimeDele();
       private bool isComplated=false;
       private int result=0;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
this.BeginInvoke(new LongTimeDele(()=>{
           Thread.Sleep(10000);////模拟耗时
     for (int i = 0; i < 100; i++)
                    {
                        result++;
                    }
        isComplated=true;
}));

while(!isComplated){
        
         if (backgroundWorker1.CancellationPending)  {
                                    e.Cancel = true;
                                    return;
                                }
                    }
}    

试验之后,不知何故在this.BeginInvoke中的循环会阻塞主线程! 即界面卡主不动了,BeginInvoke应该是一步执行的,在下功力尚浅,不知何故,还请高手指教。

于是想到了异步委托。直接上代码

cs代码

    public partial class BackgroundWorkerTest : Form
    {
      
        public BackgroundWorkerTest()
        {
            InitializeComponent();
     
        }
        /// <summary>
        /// 建立一个委托,用来调用耗时操作。返回值是int,当然也可以是string、DataTable.....
        /// </summary>
        /// <returns></returns>
        private delegate int LongTimeDele();
     

        /// <summary>
        /// doWork事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {

            //声明一个委托的实例
            var waitHandller = new LongTimeDele(
                () =>
                {
                    //线程休眠10秒,模拟耗时操作
                    Thread.Sleep(10000);
                    var sumResult = 0;
                    for (int i = 0; i < 100; i++)
                    {
                        sumResult++;
                    }
                    return sumResult;
                });
            //BeginInvoke,异步的去执行委托实例里的匿名方法。该方法返回的是一个IAsyncResult。返回值的状态会实时更新!
            var isa = waitHandller.BeginInvoke(null, null);
            //由于上面是异步,所以while的代码不会等待上面操作完成后执行,二者几乎同时执行
            //判断isa是否完成了,未完成的话(等待耗时操作)执行内部代码
            while (!isa.IsCompleted)
            {
                //判断CancellationPending属性是否为true。如果你点击了取消button,这个属性就变成true了
                if (backgroundWorker1.CancellationPending)
                {
                    //取消操作
                    e.Cancel = true;
                    return;
                }
            }
            //能执行到这里就说明操作完成了,把结果放进e中,传递给RunWorkerCompleted事件
            e.Result = waitHandller.EndInvoke(isa);
        
        }

        /// <summary>
        /// backgroundWorker执行完毕时事件,无论正常完成还是取消都会出发该事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //只要不是类型EventArgs,你想要的数据e里面应有尽有!判断是否取消了操作
            if (e.Cancelled)
            {
                MessageBox.Show("操作已取消");
                return;
                
            }
            //否则显示结果
            MessageBox.Show(e.Result.ToString());
        }


     
        /// <summary>
        /// 取消按钮事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCancle_Click(object sender, EventArgs e)
        {
            //调用   backgroundWorker的CancelAsync()方法,该方法只会将CancellationPending属性设为true,并不会实际结束线程执行
            backgroundWorker1.CancelAsync();
        }

        /// <summary>
        /// 开始按钮事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            //判断线程是否繁忙
            if (backgroundWorker1.IsBusy)
            {
                MessageBox.Show("系统繁忙");
                return;
            }
            //空闲开始异步操作
            backgroundWorker1.RunWorkerAsync();
        }
    }

 

posted @ 2014-10-15 20:25  雄风哥  阅读(3478)  评论(1编辑  收藏  举报