对使用task.Run()创建的任务调用wait
本文关键字:创建 任务 调用 wait task Run | 更新日期: 2023-09-27 18:24:56
为什么在C#中可以这样做?
var task = Task.Run (...);
await task;
Task.Run()
不是应该用于CPU绑定的代码吗?为此调用await
有意义吗?
也就是说,在调用Task.Run
之后,我了解到任务正在线程池的另一个线程中运行。调用await
的目的是什么?仅仅调用task.Wait()
不是更有意义吗?
最后一个问题,我的第一印象是await
只用于async
方法。Task.Run()
返回的任务是否常用?
编辑。这也让我想知道,为什么我们有Task.Wait ()
而没有Task.Await()
。我的意思是,为什么Wait()
使用方法,await
使用keyworkd。在这两种情况下使用一种方法不是更一致吗?
使用Wait
根本没有意义。如果你只想让另一个线程坐在那里无所事事地等待它,那么启动一个新线程来做工作是没有意义的。这两个线程中唯一明智的选择是await
。等待任务是完全明智的,因为它允许原始线程继续执行。
await
——任何类型的Task
(在正确的上下文中)都是明智的,无论它来自哪里。等待中的async
方法没有什么特别之处。事实上,在每一个异步程序中,都需要有不使用async
关键字的异步方法;如果每个await
都在等待一个async
方法,那么你将永远没有地方可以开始。
这里有几个不错的答案,但从更哲学的角度来看。。。
如果您有大量的CPU绑定工作要做,最好的解决方案通常是任务并行库,即Parallel
或并行LINQ。
如果您有I/O绑定的工作要做,最好的解决方案通常是围绕自然异步实现(例如Task.Factory.FromAsync
)构建的async
和await
代码。
Task.Run
是一种执行单个CPU绑定代码的方法,并从调用线程的角度将其视为异步代码。也就是说,如果你想做CPU绑定的工作,但又不想让它干扰UI。
构造await Task.Run
是连接两个世界的一种方式:让UI线程排队处理CPU绑定的工作,并异步处理它。这也是IMO桥接异步和并行代码的最佳方式,例如await Task.Run(() => Parallel.ForEach(...))
。
为什么Wait()使用方法,Wait使用关键字。
await
之所以是一个关键字,是因为他们希望启用模式匹配。任务并不是唯一的"可获得的"。WinRT有自己的"异步操作"概念,它是不可使用的,Rx可观察序列是不可访问的,Task.Yield
返回一个不可访问任务,这使您能够在必要时创建自己的可访问项(例如,如果您想在高性能套接字应用程序中避免Task
分配)。
是的,它很常见,推荐使用。await
允许异步等待任务(或任何可用任务)。的确,它主要用于自然异步操作(例如I/O),但它也用于卸载要使用Task.Run
在不同线程上完成的工作,并异步等待它完成。
使用Wait
不仅会阻塞调用线程,因此一开始就无法实现使用Task.Run
的目的,而且在具有单线程同步上下文的GUI环境中,它还可能导致死锁。
最后一个问题,我的第一印象是wait是专门用于异步方法的
一个方法是否真的用async
修饰符标记是一个实现细节,.Net中大多数"根"任务返回方法实际上并不是async
方法(Task.Delay
就是一个很好的例子)。
仅仅调用task是没有意义的。等待()?
不,如果您调用Wait
,则涉及两个线程,ThreadPool中的一个工作线程正在为您工作(假设任务是CPU绑定的),并且您的调用线程也将被阻止。
为什么要阻塞调用线程?如果调用线程是UI线程,结果会太糟糕!此外,如果你立即拨打Task.Run
,然后再拨打Task.Wait
,情况也会变得更糟。这并不比同步调用委托更好。在启动Task之后立即调用Wait
没有任何意义。
您几乎不应该使用Wait
,而应该始终使用await
并释放调用线程。
它非常常见,对以下情况非常有用(大大简化了;例如,生产代码需要异常处理):
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'关键字时都可以暂停该状态机,然后在等待回调时恢复,以指示已完成。这是一个简化的描述(异常传播和其他细节使事情复杂化),但关键是"等待"所做的远不止是调用Task上的方法。
在以前的C#版本中,最接近这种"async/await"魔术的构造是使用"yield return"来实现IEnumerable<T>
。对于枚举和异步方法,都需要一种暂停和恢复方法的方法。async/await关键字(以及相关的编译器支持)从可恢复方法的基本思想开始,然后添加了一些强大的功能,如异常的自动传播、通过同步上下文调度回调(最有助于将代码保留在UI线程上)以及所有粘合代码的自动实现,以设置继续回调逻辑。