当我构造一个类的实例时,我想触发令牌更新函数(async
方法)并让它在后台运行(我保留对返回的{{1的引用) }})。
稍后,当用户触发请求时,我想等待Task
。
让我们假设Task
在1秒后完成,并且用户在2秒后触发请求(这意味着Task
已完成)。
处理用户Task
await
的请求的方法,它会立即获得该值吗?毕竟,Task
已完成并保留该值。
答案 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
,因为它是在最初询问此问题后添加的。
Task
和Task<T>
,正如其他人所指出的,您可以
等待Task
(或完成Task
)多次。如果任务有
运行到完成,它会立即返回。ValueTask
或ValueTask<T>
,则无法再等待
不止一次。 .NET中引入了ValueTask
和ValueTask<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() 永远不要再使用它,例如
以及来自ValueTask
和ValueTask<T>
的MSDN文档中:
以下操作绝对不可在 ValueTask实例:
- 多次等待实例。
- 多次调用AsTask。
- 尚未完成操作时使用.Result或.GetAwaiter()。GetResult(),或多次使用它们。
- 使用这些技术中的一种以上来使用实例。
如果执行上述任何一项操作,结果将是不确定的。
答案 3 :(得分:0)
你将获得移民结果。任务完成后,其结果属性将包含结果并保留它。