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

joh*_*hni 9 .net c# asynchronous async-await

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

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

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

处理用户请求await的方法是Task,它会立即获得值吗?毕竟,Task完成并保持价值.

Dav*_*ine 10

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

是.您可以将其视为懒惰,如果您await已完成的任务立即返回.您可以在不同的线程上多次等待它,并且只有在得到结果(或出现故障)后才会返回.

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


Dav*_*ack 6

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

\n\n
    \n
  • 关于Task和,正如其他人所指出的,您可以多次Task<T>等待 a Task(或已完成)。Task如果任务已\n运行完成,它将立即返回。
  • \n
  • 但是,如果您使用 或ValueTaskValueTask<T>则不能多次等待\n。 ValueTaskValueTask<T>在 .NET\nCore 2.0 中引入,用于避免/最小化性能敏感区域的内存分配。
  • \n
\n\n

摘自 Stephen Toub 在 Dotnet 博客上的内容:

\n\n
\n

但是,由于 ValueTask 和 ValueTask 可能包装可重用的对象,因此与 Task 和 Task 相比,如果有人偏离等待它们的所需路径,它们的消耗实际上会受到很大的限制。一般来说,\n 不应在 ValueTask /\n ValueTask 上执行以下操作:

\n\n
    \n
  • 多次等待 ValueTask/ValueTask。底层对象可能已被回收并被另一个操作使用。相比之下,任务/任务永远不会从完成状态转换为未完成状态,因此您可以根据需要多次等待它,并且每次都会得到相同的答案。
  • \n
  • 同时等待 ValueTask/ValueTask。底层对象期望一次仅处理来自单个使用者的单个回调,并且尝试同时等待它可能很容易引入竞争条件和微妙的程序错误。它\xe2\x80\x99s\n 也只是上述错误操作的一个更具体的情况:\xe2\x80\x9cawaiting a\n ValueTask / ValueTask 多次。\xe2\x80\x9d 相反,Task /\n Task支持任意数量的并发等待。
  • \n
  • 当操作尚未完成\xe2\x80\x99t 时使用\n .GetAwaiter().GetResult()。在操作完成之前,\n IValueTaskSource / IValueTaskSource 实现不需要\n 支持阻塞,并且可能\xe2\x80\x99t,因此\n 这样的操作本质上是一种竞争条件,并且不太可能\n 以调用者的方式运行打算。相反,Task / Task\n 确实启用了此功能,阻止调用者直到任务完成。
  • \n
\n\n

如果您有一个 ValueTask 或一个 ValueTask,并且需要执行以下操作之一,则应使用 .AsTask() 获取 Task / Task\n,然后对生成的任务对象进行操作。此后,您不应再与该 ValueTask / ValueTask 交互。

\n\n

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

\n
\n\n

ValueTask以及来自和的 MSDN 文档ValueTask<T>

\n\n
\n

切勿在 ValueTask 实例上执行\n以下操作:

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

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

\n
\n\n

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

\n


Man*_*ert 5

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

var result = await Task.FromResult(5);
Debug.Assert(result == 5);
Run Code Online (Sandbox Code Playgroud)

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

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