在UI线程上报告后台线程进度

时间:2013-08-02 12:13:48

标签: c# .net multithreading

我想实现以下结果 - UI线程注册我耗时操作的进度更改事件,然后运行方法“DoOperationAsync()”。然后,操作将报告进度更改,但是:必须在UI线程上调用该事件,我遇到了问题。该事件触发,但是当我尝试更新UI时,我需要使用Dispatcher,因为该事件是从执行该操作的线程触发的。我觉得我的图书馆不应该强迫开发人员提前思考并在任何地方使用调度员。

基本上我想做BackgroundWorker的工作。 BackgroundWorker如何在创建它的线程上触发ProgressChanged事件?

3 个答案:

答案 0 :(得分:4)

如果您使用的是.NET 4.5,则可以访问最新版本的TPL更改,其中包括IProgress<T>接口及其具体实现Progress<T>。该界面旨在支持两个异步任务之间的进度报告,特别是您所追求的后台到UI线程报告。

接口本身很简单,将Report(T)方法定义为将类型T的进度更新传递给其他任务的机制。当您有一些要报告的进度时,您将调用该操作。如果您想传递百分比进度,可以将0.1传递给IProgress<float>个实例以报告10%的进度。

private async Task BackgroundWorkAsync(IProgress<float> progress)
{
    ...

    progress.Report(0.1); // 10%

    ...

    progress.Report(1.0); // 100%
}

UI线程应该创建具体的Progress<T>实例并将其传递给后台任务的范围。 Progress<T>提供了您可以订阅的ProgressChanged事件,但通常您会在每次更新进度时将操作传递给构造函数:

var progress = new Progress(value => // set progress bar);

await this.BackgroundWorkAsync(progress);

这是一个粗略的例子,但它显示了Progress<T>如何根据上下文同步回调的神奇之处,在这种情况下,它将是UI线程。

答案 1 :(得分:2)

BackgroundWorker使用Event-based asynchronous pattern

在内部,它使用class AsyncOperation的实例来引发事件。

具体来说,它调用AsyncOperation.Post()来在相应的线程或上下文中引发事件。

您应该能够使用库代码执行此操作。

答案 2 :(得分:0)

很久以前,并没有代码记住我是如何做到的,但这是我所做的基础。在我创建后台工作程序的UI对象中,我还创建了一个在报告进度时触发的方法。然后,在后台工作器中,我设置了可以读取的公共属性并将其放入我的UI中。

public class myUIFormControlWhatever
{
   ...
   public void CallTheBackgroundWorker()
   {
      myBackgroundWorker bgw = new myBackgroundWorker();
      // attach "listening" when the background worker reports changes
      bgw.ProgressChanged += thisObjectShowChangedProgress;
      bgw.RunWorkerAsync();
   }

   protected void thisObjectShowChangedProgress( object sender, ProgressChangedEventArgs e )
   {
      this.SomeTextShownOnUI = ((myBackgroundWorker)sender).ExposedProperty;
   }
}


public class myBackgroundWorker : BackgroundWorker
{
   public myBackgroundWorker()
   {
      WorkerReportsProgress = true;
      // hook up internal to background worker any strings
      // you want to expose once reporting and any other listeners are out there.
      ProgressChanged += StatusUpdate;
   }

   protected void StatusUpdate( object sender, ProgressChangedEventArgs e )
   {
      // set property to what you want any other listeners to grab/display
      ExposedProperty = "something you are handling internally to background worker";
   }

   public string ExposedProperty
   { get; protected set; }

}

同样,大部分是从内存中查找事件处理程序参数参数我记不起签名了。因此,UI会创建后台工作程序,但通过连接到“ProgressChanged”事件来侦听任何报告的更改。因此,一旦后台工作人员做了这件事,UI组件就会通过查看从“对象发送者”参数(后台工作者本身)读取的可见属性来处理它自己的段。