2015-02-10 92 views
12

此代码片段来自Stephen Cleary's blog,并给出了使用Task.Run时如何报告进度的示例。我想知道为什么在更新UI时没有交叉线程问题,我的意思是为什么不需要调用?Task.Run和UI进度更新

private async void button2_Click(object sender, EventArgs e) 
{ 
    var progressHandler = new Progress<string>(value => 
    { 
     label2.Text = value; 
    }); 
    var progress = progressHandler as IProgress<string>; 
    await Task.Run(() => 
    { 
     for (int i = 0; i != 100; ++i) 
     { 
      if (progress != null) 
       progress.Report("Stage " + i); 
      Thread.Sleep(100); 
     } 
    }); 
    label2.Text = "Completed."; 
} 
+0

@newbieguy字符串连接自动调用ToString()方法 – 2018-03-01 07:11:27

回答

19

Progress<T>当它被实例化捕捉当前SynchronisationContext。无论何时您致电Report,它都会将其秘密委托给捕获的上下文。在该示例中,捕获的上下文是UI,这意味着不会发生任何异常。

+0

你知道给线程优先级吗?即:正常,背景,ApplicationIdle等。 – Kelly 2017-05-17 02:05:21

+0

@Kelly调度程序优先级为正常。请参阅此处的备注部分https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatchersynchronizationcontext.post(v=vs.110).aspx – Gusdor 2017-05-17 05:46:02

8

构造函数Progress<T>捕获当前SynchronizationContext对象。

SynchronizationContext类是抽象涉及的线程模型的细节的工具。也就是说,Windows窗体将使用Control.Invoke,在WPF将使用Dispatcher.Invoke

progress.Report对象被调用时,Progress对象本身知道它应该使用捕获SynchronizationContext运行其委托。

换句话说,它的工作原理是因为Progress被设计来处理,而开发人员不必明确地说出来。

4

看来你是因为事实证明这跨线程机制的一部分是从开发商的眼睛隐藏的,所以你只需要“采取和使用”困惑:http://blogs.msdn.com/b/dotnet/archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx

我们推出了IProgress接口使您能够创建显示进度的体验 。该界面公开了一个Report(T) 方法,异步任务调用该方法报告进度。您在异步方法的签名中公开此接口,并且调用者必须提供实现此接口的对象。在一起,任务 和调用者创建了一个非常有用的链接(并且可以在不同的线程上运行 )。

我们还提供了Progress类,它是 IProgress的实现。我们鼓励您在您的 实施中使用Progress,因为它可以处理所有关于保存 的簿记并恢复同步上下文。进度显示 事件和一个动作回调,当任务 报告进度时会调用这个回调。这种模式使您能够编写简单的代码,以便在发生更改时对其进行更改。 IProgress和 Progress一起提供了一种将 后台任务的进度信息传递给UI线程的简单方法。

还有一两件事提:进度通知将后调用作业的部分已经完成,不只是在那一刻。所以,如果你的UI线程空闲并且你有空闲的CPU内核,延迟几乎为零。如果您的UI线程繁忙,则在UI线程回到空闲状态(不管您的计算机有多少备用CPU核心)之前,不会调用该通知。