对使用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。在这两种情况下使用一种方法不是更一致吗?

对使用task.Run()创建的任务调用wait

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

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

这里有几个不错的答案,但从更哲学的角度来看。。。

如果您有大量的CPU绑定工作要做,最好的解决方案通常是任务并行库,即Parallel或并行LINQ。

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

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线程上)以及所有粘合代码的自动实现,以设置继续回调逻辑。