【问题标题】:BackgroundWorker not exiting loopBackgroundWorker 没有退出循环
【发布时间】:2014-11-28 13:42:32
【问题描述】:

我有一个 BackgroundWorker: BackgroundWorker worker;

private void Form1_Load(object sender, EventArgs e)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;

worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged += 
          new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerCompleted += 
         new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
}

DoWork 事件

void worker_DoWork(object sender, DoWorkEventArgs e)
{
int percentFinished = (int)e.Argument;
while (!worker.CancellationPending && percentFinished < 100)
{
 percentFinished++;
 worker.ReportProgress(percentFinished);

 //here I start my operation
 //operation....
 //operation end

}
e.Result = percentFinished;
}

进度改变

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

完成

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Asynchroner Thread kam bis zum Wert:
"+e.Result.ToString());
btnStartEnd.Text = "Starten";
}

最后是我的按钮:

private void btnStartEnd_Click(object sender, EventArgs e)
{
   if (worker.IsBusy)
   {
   worker.CancelAsync();
   btnStartEnd.Text = "Starten";
   }
else
   {
     if (progressBar1.Value == progressBar1.Maximum)
      {
      progressBar1.Value = progressBar1.Minimum;
      }
    worker.RunWorkerAsync(progressBar1.Value);
    btnStartEnd.Text = "Stoppen";
   }
}

此代码有效,但我的操作会出现循环,直到百分比为 100,因此操作开始 100 次,因此需要 100 倍的时间。

目标应该是操作只开始一次,百分比从 1 到 100 计数。

也许我理解错了,但是工人怎么知道操作完成了多远?该值应发送到进度条以进行可视化。

【问题讨论】:

  • 不幸的是,回答您的问题的唯一方法是准确了解您的操作中发生了什么(在问题中仅由 cmets 表示)。当然,您不想仅仅为了进步而循环 100 次。但是您需要操作本身具有可以报告进度的有意义的间隔。您的问题中没有任何内容表明它是如何工作的,因为它根本不包含有关操作本身的任何细节。

标签: c# backgroundworker


【解决方案1】:

通常你不会在 DoWork 方法中添加循环 例如,如果您想从文件系统加载 100 个文件并报告进度,它可能如下所示:

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    for(int i = 0; i < 100; i++)
    {
        // Load file and do something with the content
        ...
        // Report the progress which causes the ProgressChanged event to be fired
        // And update progressbar with the UI thread
        worker.ReportProgress(i);                   
    }
}

如果你只有一个长时间运行的操作需要在 DoWork 方法中执行,它需要异步执行

这是一个如何在 .NET 中异步调用操作的示例:

Action action = () =>
{
    for(int i = 0; i <100; i++)
    {
        Console.WriteLine(String.Format("Step {0} of long running operation", i));
        Thread.Sleep(1000);
    }
};

var r = action.BeginInvoke(null, null);
while(!r.IsCompleted)
{
    Console.WriteLine("Waiting...");
    Thread.Sleep(100);
}

但是在 .NET 中还有很多方法可以做到这一点。例如:

【讨论】:

  • 谢谢,很清楚。我只有一个长时间运行的操作,那么如何在返回进度条的情况下异步执行它,以便我的表单仍在运行?
  • 我将答案扩展了一点,包括一个如何异步调用方法的简单示例
  • @katz 我只有一个长时间运行的操作,那么如何在返回进度条的同时异步执行它,以便我的表单仍在运行? DoWork 方法已经相对于 UI 线程异步执行。如果您无法将操作分成更小的部分或以某种方式提供进度估计值,那么您就完全不走运了,您只能显示一些“正在进行的工作”动画。
【解决方案2】:

也许我理解错了,但是工人怎么知道操作完成了多远?该值应发送到进度条以进行可视化。

BackgroundWorker 类对您的操作及其进度一无所知。确定何时报告进度是您的工作。

一般来说,后台工作人员的工作流程如下所示:

  • UI 线程调用RunWorkerAsync
  • DoWork 事件处理程序在不同的线程上调用。在事件处理期间,您可以使用ReportProgress 方法报告进度
  • 如果您报告进度,则在 UI 线程上调用 ProgressChanged 事件处理程序。例如,您可以在此处更新进度条。
  • DoWork 事件的事件处理程序退出时,会引发RunWorkerComplete 事件。

现在为什么BackgroundWorker 的每个示例都有一个 for 循环?因为它很容易编写,并且测量 for 循环的进度是微不足道的。不幸的是,这通常对不同类型的操作没有用处。

如果您长时间运行的操作处理 N 个文件,那么很明显您可以在每个项目之后将进度条更新 1/N。这就是 for 循环示例的作用。

但是,如果您只有一个长时间运行的操作,那么您根本没有任何机会获得进度除非操作本身支持报告它,或者如果您可以以某种方式估计进度。 p>

BackgroundWorker 不能神奇地为长时间运行的操作提供进度条。它只允许您在后台运行操作。

【讨论】:

  • 感谢您的解释
猜你喜欢
  • 1970-01-01
  • 2014-05-14
  • 2021-12-06
  • 2013-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多