此代码段来自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.";
}
答案 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核心)。