首页 新闻 会员 周边

使用BackgroundWorker时,出现ProgressChanged延迟的现象,请帮分析下原因

0
悬赏园豆:40 [已解决问题] 解决于 2013-04-04 22:55

使用BackgroundWorker时,出现ProgressChanged延迟的现象,即我在西面代码的Completed事件中抛出完成时的messageBox,但是因为ProgressChanged的延迟,我的ProgressBar进度条还没满就弹出了对话框,然后progressBar才满……这问题请帮我分析下。

虽说是AsyncOperation.Post()插入消息会延迟,但是ProgressChanged消息是在Completed消息之前插入的啊,至少执行上应该保持队列去执行消息吧????

代码如下:

View Code
    public partial class BackgroundWorker_Test : Form
    {
        private BackgroundWorker worker2 = null;
        public BackgroundWorker_Test()
        {
            InitializeComponent();

            worker2 = new BackgroundWorker();
            worker2.DoWork += backgroundWorker2_DoWork;
            worker2.ProgressChanged += backgroundWorker2_ProgressChanged;
            worker2.RunWorkerCompleted += RunWorkerCompleted;

            worker2.WorkerSupportsCancellation = true;
            worker2.WorkerReportsProgress = true;
        }

        private void btn_Cancel_Click(object sender, EventArgs e)
        {
            if (worker2.IsBusy)
            {
                worker2.CancelAsync();
            }
        }

        private void btn_Start_Click(object sender, EventArgs e)
        {
            if (!worker2.IsBusy)
            {
                this.progressBar1.Value = 0;
                worker2.RunWorkerAsync();
            }
            else
            {
                MessageBox.Show("正在执行操作,请稍后");
            }
        }

        private void backgroundWorker2_DoWork(object sender,
            DoWorkEventArgs e)
        {
            BackgroundWorker worker2 = sender as BackgroundWorker;
            int sum = 0;
            try
            {
                for (int i = 1; i <= 100; i++)
                {
                    if (worker2.CancellationPending)
                        break;
                    Thread.Sleep(20);
                    sum += i;
                    // 进度报告
                    worker2.ReportProgress(i);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            if (worker2.CancellationPending)
            {
                e.Cancel = true;
            }
            else
            {
                e.Result = sum;
            }
        }

        private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.progressBar1.Value = e.ProgressPercentage;
        }

        private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                MessageBox.Show(e.Error.Message);
            }
            else if (e.Cancelled)
            {
                MessageBox.Show("操作已被取消");
            }
            else
            {
                MessageBox.Show(String.Format("操作已完成,结果为:{0}", e.Result));
            }
        }
    }
问题补充:

如图:

滴答的雨的主页 滴答的雨 | 老鸟四级 | 园豆:3660
提问于:2013-03-29 11:52
< >
分享
最佳答案
0

这是一个不错的问题。

首先,这个还是不难理解的,因为ReportProgress之后,具体显示进度,是要切换到主线程来做,主线程是订阅了这个事件。所以这肯定是一个异步的,也就是说,DoWork里面ReportProgress后,并不会等待真正地更新完进度。所以这样就可能会出现你提到的问题。

然后,我修改了一下你的代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private BackgroundWorker worker2 = null;

        public Form1()
        {
            InitializeComponent();

            worker2 = new BackgroundWorker();
            worker2.DoWork += backgroundWorker2_DoWork;
            worker2.ProgressChanged += backgroundWorker2_ProgressChanged;
            worker2.RunWorkerCompleted += RunWorkerCompleted;

            worker2.WorkerSupportsCancellation = true;
            worker2.WorkerReportsProgress = true;


        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (!worker2.IsBusy)
            {
                this.progressBar1.Value = 0;
                waitHandler = new AutoResetEvent(false);

                worker2.RunWorkerAsync();
            }
            else
            {
                MessageBox.Show("正在执行操作,请稍后");
            }

        }


        AutoResetEvent waitHandler = null;

        private void backgroundWorker2_DoWork(object sender,
            DoWorkEventArgs e)
        {

            

            BackgroundWorker worker2 = sender as BackgroundWorker;
            int sum = 0;
            try
            {
                for (int i = 1; i <= 100; i++)
                {
                    if (worker2.CancellationPending)
                        break;
                    Thread.Sleep(20);
                    sum += i;
                    // 进度报告
                    worker2.ReportProgress(i);

                    waitHandler.WaitOne();

                }
            }
            catch (Exception ex)
            {
                throw ex;
            }


            if (worker2.CancellationPending)
            {
                e.Cancel = true;
            }
            else
            {
                e.Result = sum;
            }

        }

        private void backgroundWorker2_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.progressBar1.Value = e.ProgressPercentage;

            waitHandler.Set();
        }

        private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {

            if (e.Error != null)
            {
                MessageBox.Show(e.Error.Message);
            }
            else if (e.Cancelled)
            {
                MessageBox.Show("操作已被取消");
            }
            else
            {
                MessageBox.Show(String.Format("操作已完成,结果为:{0}", e.Result));
            }
        }
    }
}

 

我用到了AutoResetEvent来控制,就是说强制地要求ReportProgress操作发出后,要等待事件响应回来。这样从逻辑上说,就可以完全保证你所提到的先后顺序问题。

 

但实际运行起来,我还是发现会有一点点奇怪,就是看起来那个进度条还没有更新完,那个对话框就弹出来了。我这里只是猜想窗体元素重画的时间可能确实比较长,也就是说,其实是更新完了,但用户看到的可能有一点点延时吧。

 

没有完美地解决这个问题,但我想这个思路可以给你参考的

收获园豆:40
陈希章 | 老鸟四级 |园豆:2538 | 2013-03-30 10:48

谢谢这个思路,我这边先留着看下。过两天我写一篇基于事件异步编程的博文,到时文中会提到这个问题,看看有没有别的想法

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-03-30 15:16

哈哈,这个问题就先这样解决了,谢谢你的回答。

滴答的雨 | 园豆:3660 (老鸟四级) | 2013-04-04 22:54
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册