Task.Run和UI进度更新

时间:2015-02-10 11:46:12

标签: c# asynchronous progress

此代码段来自Stephen Cleary's blog,并举例说明了在使用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.";
}

3 个答案:

答案 0 :(得分:24)

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

答案 1 :(得分:11)

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

SynchronizationContext类是一个提取所涉及的线程模型细节的工具。也就是说,在Windows窗体中它将使用Control.Invoke,在WPF中它将使用Dispatcher.Invoke等。

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

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

答案 2 :(得分:5)

看起来你很困惑,因为这个跨线程机器的一部分是隐藏在开发人员眼中的,所以你只需要采取和使用&#34;:http://blogs.msdn.com/b/dotnet/archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx

  

我们引入了IProgress接口,使您可以创建   显示进度的经验。此界面公开报告(T)   方法,异步任务调用以报告进度。你暴露了这个   async方法签名中的接口,以及调用者必须的   提供实现此接口的对象。一起完成任务   并且调用者创建了一个非常有用的链接(并且可以运行   不同的线程)。

     

我们还提供了Progress类,它是一个实现   IProgress。我们鼓励您使用Progress   实施,因为它处理所有关于保存的簿记   并恢复同步上下文。进步暴露了两者   事件和Action回调,在任务时调用   报告进展。这种模式使您可以简单地编写代码   在发生变化时对变化做出反应。 IProgress和   进度提供了一种从a传递进度信息的简便方法   UI线程的后台任务。

还有一件事需要提及:在部分工作完成后会调用进度通知,不仅仅是在那一刻。因此,如果您的UI线程处于空闲状态且您有备用CPU核心,则延迟将几乎为零。如果您的UI线程忙,则在UI线程恢复空闲之前不会调用通知(无论您的计算机有多少备用CPU核心)。