【问题标题】:Report Background Thread Progress on UI Thread报告 UI 线程的后台线程进度
【发布时间】:2013-08-02 12:13:48
【问题描述】:

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

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

【问题讨论】:

  • 不,您不需要将 Dispatcher 与后台工作人员 reportsprgress 一起使用。按照 MSDN 上的示例进行操作。
  • 再次阅读问题 - 我没有使用 BackgroundWorker。
  • 为什么不使用 BackgroundWorker?
  • BackgroundWorker 使用AsyncOperation.Post 方法在适当的线程上调用事件。

标签: c# .net multithreading


【解决方案1】:

如果您使用 .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&lt;T&gt; 实例并将其传递到后台任务的范围内。 Progress&lt;T&gt; 提供了一个可以订阅的 ProgressChanged 事件,但通常你将一个操作传递给构造函数,以便在每次更新进度时调用:

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

await this.BackgroundWorkAsync(progress);

这是一个粗略的例子,但它展示了Progress&lt;T&gt; 如何根据上下文同步回调的魔力,在本例中是 UI 线程。

【讨论】:

  • 听起来不错,我将在我未来的一些项目中尝试一下,但我目前正在开发的应用程序也必须在 Windows XP 上运行。所以我坚持使用 4.0。
【解决方案2】:

BackgroundWorker 使用 Event-based asynchronous pattern

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

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

你应该可以用你的库代码做到这一点。

【讨论】:

  • 谢谢,这正是我正在研究的。我的代码如下:pastebin.com/0SrWRvnj 但是 UI 线程仍然报告另一个线程拥有该控件的错误。
  • 在我看来,您正试图让后台线程更改进度条(或您用来跟踪进度的任何内容)。这是不允许的。您可以做以下两件事之一:从后台线程,定期在您的 UI 上触发一个发布进度数据的事件,然后 UI 呈现进度; 2) 使用Invoke 从后台线程调用UI 控件上的方法或属性,以更新进度。您不能直接从后台线程访问 UI 元素方法或属性。
  • @OndraMašitů 看看这个:msdn.microsoft.com/en-us/library/9hk12d4y.aspx 它为您提供了有关如何初始化 AsyncOperation 类以使其正常工作的更多信息。您需要使用 AsyncOperationManager 来创建 AsyncOperation。不幸的是,这东西很繁琐。
  • @fourpastmidnight 这当然是真的。请仔细阅读我的问题。
  • 总结一下:BackgroundWorker 使用 AsyncOperation,它使用 SynchronizationContext,它(在这种情况下)使用 Dispatcher。虽然您可以使用AsyncOperationSynchronizationContext 编写EAP component,但我建议您改为编写TAP component;基于任务的 API 是异步编程的未来。顺便说一句,Progress&lt;T&gt; 也使用SynchronizationContext,它(在这种情况下)使用Dispatcher。 :)
【解决方案3】:

很久以前,没有代码可以准确地记住我是如何做到的,但这是我所做工作的基础。在我创建后台工作人员的 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 组件就会通过查看可以从后台工作人员本身的“object sender”参数读取的可见属性来处理它自己的段。

【讨论】:

  • 谢谢,这确实可行,但我正在研究后台工作人员是如何做到的,它能够在最初调用它的 RunWorkerAsync 方法的线程上触发事件。请参阅我在 Matthew 的评论中的回答。我相信他是对的,只是我无法使 Post 方法正常工作。它一直在错误的线程上调用它。
  • @OndraMašitů,我理解你的问题,但我只是抛出了我过去的原则。当后台工作人员调用事件的所有“侦听器”时,UI 线程对象现在获得了自己的控制权并且可以对自己进行操作,但只是从后台工作人员中提取一个值来显示。只是另一种思维方式,也可能有助于您的最终解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-30
  • 1970-01-01
  • 2023-04-05
  • 2011-06-12
  • 1970-01-01
  • 1970-01-01
  • 2013-01-24
相关资源
最近更新 更多