【问题标题】:Task.Run and UI Progress UpdatesTask.Run 和 UI 进度更新
【发布时间】:2015-04-10 10:30:55
【问题描述】:

此代码 sn-p 来自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.";
}

【问题讨论】:

  • @newbieguy 字符串连接自动调用 ToString() 方法

标签: c# asynchronous progress


【解决方案1】:

Progress&lt;T&gt; 在实例化时捕获当前的SynchronisationContext。每当您调用Report 时,它都会秘密地将其委托给捕获的上下文。在示例中,捕获的上下文是 UI,这意味着不会发生异常。

【讨论】:

  • 你知道这个线程优先级是多少吗?即:正常、背景、ApplicationIdle 等。
  • @Kelly 调度程序优先级为正常。请参阅此处的备注部分msdn.microsoft.com/en-us/library/…
【解决方案2】:

Progress&lt;T&gt; 构造函数捕获当前的SynchronizationContext 对象。

SynchronizationContext 类是一种抽象相关线程模型细节的工具。也就是说,在 Windows 窗体中它将使用Control.Invoke,在 WPF 中它将使用Dispatcher.Invoke,等等。

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

换句话说,它之所以有效,是因为 Progress 旨在处理该问题,而无需开发人员明确说明。

【讨论】:

    【解决方案3】:

    您似乎很困惑,因为这种跨线程机制的一部分对开发人员来说是隐藏的,所以您只需要“接受并使用”:https://devblogs.microsoft.com/dotnet/async-in-4-5-enabling-progress-and-cancellation-in-async-apis

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

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

    再提一件事:进度通知将在部分工作完成后被调用,不仅仅是在那一刻。因此,如果您的 UI 线程处于空闲状态并且您有空闲的 CPU 内核,则延迟几乎为零。如果您的 UI 线程很忙,直到 UI 线程恢复空闲时才会调用通知(无论您的计算机有多少空闲 CPU 内核)。

    【讨论】:

    • &gt; If your UI thread is busy, the notification will not be invoked until the moment the UI thread is back to idle - 谢谢你的那一行,我试图从 UI 线程运行 ProgressObj.Report(),这可能不起作用,因为 UI 线程已经很忙并且不会执行Report() 直到它变得免费。
    猜你喜欢
    • 2017-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-17
    相关资源
    最近更新 更多