Task.Run和UI进度更新

Ale*_*and 23 c# asynchronous progress

此代码段来自Stephen Cleary的博客,并提供了使用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.";
}
Run Code Online (Sandbox Code Playgroud)

Gus*_*dor 28

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


Jea*_*nal 11

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

SynchronizationContext班是一个设备抽象所涉及的线程模型的细节.也就是说,在Windows Forms中它将使用Control.Invoke,在WPF中它将使用Dispatcher.Invoke,等等.

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

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


Yur*_*ula 5

看起来你很困惑,因为这个跨线程机器的一部分是隐藏在开发人员眼中的,所以你只需要"接受和使用":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)方法,异步任务调用该方法来报告进度.您在async方法的签名中公开此接口,并且调用方必须提供实现此接口的对象.任务和调用者一起创建了一个非常有用的链接(并且可以在不同的线程上运行).

我们还提供了Progress类,它是IProgress的一个实现.我们鼓励您在实现中使用Progress,因为它处理有关保存和恢复同步上下文的所有簿记.Progress公开事件和Action回调,在任务报告进度时调用它们.通过此模式,您可以编写代码,以便在进度发生变化时立即做出反应.IProgress和Progress共同提供了一种将进度信息从后台任务传递到UI线程的简便方法.

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