为什么可以在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。在两种情况下使用方法都不会更一致吗?
答案 0 :(得分:7)
使用Wait
毫无意义。没有必要启动一个新的线程来做工作,如果你只是要让另一个线程坐在那里,什么都不做等待。这两者中唯一明智的选择是await
它。等待任务是完全合理的,因为它允许原始线程继续执行。
await
任何类型的Task
(在正确的背景下)都是明智的,无论它来自哪里。关于等待async
方法没有什么特别之处。事实上,在每个异步程序中都需要有不使用async
关键字的异步方法;如果每个await
等待async
方法,那么您将永远无法开始。
答案 1 :(得分:4)
这里有几个好的答案,但从更哲学的角度来看......
如果您有许多CPU限制工作要做,最好的解决方案通常是任务并行库,即Parallel
或并行LINQ。
如果您要进行I / O绑定工作,最好的解决方案通常是async
和await
代码,这些代码是围绕自然异步实现构建的(例如,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线程上的代码)并自动执行所有胶水代码以设置延续回调逻辑。