等待已经完成的任务时会发生什么?

时间:2016-07-11 11:57:01

标签: c# .net asynchronous async-await

当我构造一个类的实例时,我想触发令牌更新函数(async方法)并让它在后台运行(我保留对返回的{{1的引用) }})。

稍后,当用户触发请求时,我想等待Task

让我们假设Task在1秒后完成,并且用户在2秒后触发请求(这意味着Task已完成)。

处理用户Task await的请求的方法,它会立即获得该值吗?毕竟,Task已完成并保留该值。

4 个答案:

答案 0 :(得分:8)

  

处理用户请求的方法等待该任务,它会立即获得该值吗?

是。您可以将其视为懒惰,如果您await已完成的任务立即返回。你可以在不同的线程上等待它几次,它只会在结果出现后返回(或者出现故障)。

出于这个原因,

Task.CompletedTask被添加为精准的。你可以await这个,它会立即返回一个成功的任务,因为它已经完成了。

答案 1 :(得分:3)

您可以使用Task.FromResult(value)创建一个已完成的任务并等待它:

var result = await Task.FromResult(5);
Debug.Assert(result == 5);

如果你有一个可以返回缓存数据但需要第一次异步获取它的方法,这很有用。

所以,是的,你可以等待已经完成的任务。

答案 2 :(得分:1)

尽管OP并未提及ValueTask,但我将这个答案添加为包含ValueTask,因为它是在最初询问此问题后添加的。

  • 关于TaskTask<T>,正如其他人所指出的,您可以 等待Task(或完成Task)多次。如果任务有 运行到完成,它会立即返回。
  • 但是,如果您使用的是ValueTaskValueTask<T>,则无法再等待 不止一次。 .NET中引入了ValueTaskValueTask<T> Core 2.0和Core 2.0用于避免/最小化对性能敏感区域的内存分配。

摘自Dotnet博客上的Stephen Toub:

但是,因为ValueTask和ValueTask可以包装可重用的 对象,实际上对它们有很大的限制 与任务和任务相比时,应该有人 偏离等待他们的期望路径。一般而言, 以下操作永远不要在ValueTask上执行/ ValueTask:

  • 多次等待ValueTask / ValueTask 。的 基础对象可能已经被回收并被使用 另一项操作。相反,任务/任务永远不会 从完整状态过渡到不完整状态,因此您可以等待 您需要多次,并且每次都会得到相同的答案 时间。
  • 同时等待ValueTask / ValueTask 。的 基础对象希望仅使用来自一个对象的单个回调 一次有一个消费者,并尝试同时等待 可能会轻易引入比赛条件和细微的程序错误。它的 也是上述错误操作的一个更具体的情况:“等待 ValueTask / ValueTask多次。”相反,任务/ 任务确实支持任意数量的并发等待。
  • 使用 .GetAwaiter()。GetResult()操作尚未完成。的 IValueTaskSource / IValueTaskSource实现无需 支持阻塞,直到操作完成为止(可能不会完成),因此 这样的操作本质上是一种竞赛条件,不太可能 表现出呼叫者想要的方式。相反,任务/任务 请启用此功能,阻止调用方,直到任务完成。

如果您具有ValueTask或ValueTask,则需要执行以下一项操作 这些事情,您应该使用.AsTask()来获得一个Task / Task 然后对该结果任务对象进行操作。在那之后,你 永远不要再与该ValueTask / ValueTask进行交互。

简短的规则是:使用ValueTask或ValueTask,您可以 应该直接等待它(可选地与 .ConfigureAwait(false))或直接在其上调用AsTask() 永远不要再使用它,例如

以及来自ValueTaskValueTask<T>的MSDN文档中:

以下操作绝对不可在 ValueTask实例:

  • 多次等待实例。
  • 多次调用AsTask。
  • 尚未完成操作时使用.Result或.GetAwaiter()。GetResult(),或多次使用它们。
  • 使用这些技术中的一种以上来使用实例。

如果执行上述任何一项操作,结果将是不确定的。

您可以详细了解ValueTaskValueTask<T> herehere

答案 3 :(得分:0)

你将获得移民结果。任务完成后,其结果属性将包含结果并保留它。