等待已完成的任务时会发生什么情况

本文关键字:什么情况 已完成 任务 等待 | 更新日期: 2023-09-27 17:56:14

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

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

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

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

等待已完成的任务时会发生什么情况

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

是的。你可以把它想象成懒惰,如果你await已经完成的任务,它会立即返回。您可以在不同的线程上多次等待它,并且只有在获得结果(或出错)后才会返回。

正是出于这个原因,Task.CompletedTask被添加为一个好东西。您可以await这样做,它会立即返回一个成功的任务,因为它已经完成。

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

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

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

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

虽然 OP 没有提到ValueTask,但我添加这个答案是为了包括ValueTask,因为它是在最初提出这个问题之后添加的。

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

摘自Stephen Toub在Dotnet博客上:

但是,因为 ValueTask 和 ValueTask 可能包装可重用 对象,实际上对它们的约束很大 与任务和任务相比,应该有人 偏离等待他们的预期道路。一般来说, 以下操作永远不应该在 ValueTask/ 价值任务:

  • 多次等待 ValueTask/ValueTask。这 基础对象可能已被回收并正在使用 另一个操作。相比之下,任务/任务永远不会 从完成状态转换为不完整状态,因此您可以等待它 很多次,只要你需要,并且总是会得到相同的答案 时间。
  • 同时等待 ValueTask/ValueTask 。这 基础对象期望仅使用来自 一次一个使用者,并尝试同时等待它 很容易引入竞争条件和微妙的程序错误。它 也只是上述不良操作的更具体案例:"等待 价值任务/价值任务多次。相比之下,任务/ 任务支持任意数量的并发等待。
  • 用 .GetAwaiter()。GetResult() 当操作尚未完成时。这 IValueTaskSource/IValueTaskSource 实现不需要 支持阻止,直到操作完成,并且可能不会,所以 此类操作本质上是一种争用条件,不太可能 按照调用方的意图行事。相比之下,任务/任务 请启用此功能,阻止调用方,直到任务完成。
如果您有 ValueTask

或 ValueTask 并且需要执行以下操作之一 这些东西,你应该使用.AsTask() 获取任务/任务 ,然后对该生成的任务对象进行操作。在那之后,你 应该永远不要再与该价值任务/价值任务交互。

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

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

切勿在 ValueTask 实例:

  • 多次等待实例。
  • 多次调用 AsTask。
  • 用。结果或 .GetAwaiter()。GetResult() 当操作尚未完成时,或多次使用它们。
  • 使用
  • 其中的多种技术来使用实例。

如果执行上述任一操作,则结果未定义。

您可以在此处和此处阅读有关ValueTaskValueTask<T>的更多信息。

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