调用等待使用Task.Run()创建的任务

时间:2014-09-29 19:33:49

标签: c# .net task-parallel-library async-await

为什么可以在C#中执行此操作?

var task = Task.Run (...);
await task;

是不是Task.Run()应该用于CPU绑定代码?为此调用await是否有意义?

即,在调用Task.Run之后,我理解该任务正在线程池的另一个线程中运行。致电await的目的是什么?只要拨打task.Wait()

就没有意义了

最后一个问题,我的第一印象是await旨在专门用于async方法。将它用于Task.Run()返回的任务是否常见?

EDIT。这也让我想知道,为什么我们有Task.Wait ()而不是Task.Await()。我的意思是,为什么Wait()使用方法,await使用keyworkd。在两种情况下使用方法都不会更一致吗?

5 个答案:

答案 0 :(得分:7)

使用Wait毫无意义。没有必要启动一个新的线程来做工作,如果你只是要让另一个线程坐在那里,什么都不做等待。这两者中唯一明智的选择是await它。等待任务是完全合理的,因为它允许原始线程继续执行。

await任何类型的Task(在正确的背景下)都是明智的,无论它来自哪里。关于等待async方法没有什么特别之处。事实上,在每个异步程序中都需要有不使用async关键字的异步方法;如果每个await等待async方法,那么您将永远无法开始。

答案 1 :(得分:4)

这里有几个好的答案,但从更哲学的角度来看......

如果您有许多CPU限制工作要做,最好的解决方案通常是任务并行库,即Parallel或并行LINQ。

如果您要进行I / O绑定工作,最好的解决方案通常是asyncawait代码,这些代码是围绕自然异步实现构建的(例如,Task.Factory.FromAsync)。

Task.Run是一种执行块CPU绑定代码的方法,从调用线程的角度来看它是异步的。即,如果你想做CPU限制的工作,但不要让它干扰UI。

构造await Task.Run是一种桥接两个世界的方法:让UI线程排队CPU绑定工作并异步处理它。这也是IMO桥接异步和并行代码的最佳方式,例如await Task.Run(() => Parallel.ForEach(...))

  

为什么一个方法用于Wait()和一个关键字用于等待。

await是关键字的一个原因是因为他们想要启用模式匹配。任务不是那里唯一的“等待者”。 WinRT有自己的“异步操作”概念是等待的,Rx可观察序列是等待的,Task.Yield返回一个非等待的任务,这使你能够在必要时创建自己的等待(例如if you want to avoid Task allocations in high-performance socket applications })。

答案 2 :(得分:1)

是的,它很常见且值得推荐。 await允许等待任务(或任何等待的)异步。它确实主要用于自然异步操作(例如I / O),但它也用于使用Task.Run在不同的线程上卸载工作。异步等待它完成。

使用Wait不仅会阻塞调用线程,因此首先会失去使用Task.Run的目的,它还可能导致具有单线程同步上下文的GUI环境中的死锁。

  

最后一个问题,我的第一印象是await旨在专门用于异步方法

方法是否实际上用async修饰符标记是一个实现细节,而且大多数" root" .Net中的任务返回方法实际上不是async个(Task.Delay就是一个很好的例子)。

答案 3 :(得分:1)

  

调用task.Wait()?

会更有意义

不,如果你调用Wait,那里有两个线程, ThreadPool 中的一个工作线程正在为你工作(假设任务是CPU绑定的),并且你的调用线程也会被阻止。

为什么要阻止调用线程?如果调用线程是UI线程,结果将太糟糕!此外,如果您立即致电Task.Run,紧接着Task.Wait,那么您的情况也会变得更糟。它并不比同步调用委托更好。在开始任务后立即调用Wait毫无意义。

你几乎不应该使用Wait,总是更喜欢await并释放调用线程。

答案 4 :(得分:0)

对于像(大大简化;生产代码需要例外处理)这样的情况来说,这是非常常见和有用的:

async void OnButtonClicked()
{
   //... Get information from UI controls ...
   var results = await Task.Run(CpuBoundWorkThatShouldntBlockUI);
   //... Update UI based on results from work run in the background ...
}

关于稍后编辑的评论,'await'不是方法调用。它是一个关键字(仅在标记为'async'的方法中允许,为清楚起见),编译器使用该关键字来决定如何实现该方法。在引擎盖下,这涉及将方法过程重写为状态机,每次使用'await'关键字时都可以暂停,然后在等待回调的任何内容时恢复,以表明它已完成。这是一个简化的描述(异常传播和其他细节使事情复杂化),但关键是“等待”不仅仅是在任务上调用方法。

在以前的C#版本中,与'async / await'魔法最接近的构造是使用'yield return'来实现IEnumerable<T>。对于枚举和异步方法,您需要一种暂停和恢复方法的方法。 async / await关键字(以及相关的编译器支持)从这个可恢复方法的基本概念开始,然后添加一些强大的功能,如自动传播异常,通过同步上下文调度回调(主要用于保持UI线程上的代码)并自动执行所有胶水代码以设置延续回调逻辑。