什么时候适合使用task.Result替代await?含ContinueWith场景相关疑问
Task.Result的合法使用场景说明
大家平时不推荐直接用Task.Result,核心是怕调用时任务还未完成,导致线程阻塞甚至死锁,但只要满足任务已经100%进入完成状态的前提,用Result是完全安全的,以下是典型的合法场景:
1. ContinueWith延续任务内部读取前值任务结果
你给出的示例代码用法是完全正确的:
var task = Task.Run( () => { DateTime date = DateTime.Now; return date.Hour > 17 ? "evening" : date.Hour > 12 ? "afternoon" : "morning"; }); await task.ContinueWith( antecedent => { Console.WriteLine($"Good {antecedent.Result}!"); Console.WriteLine($"And how are you this fine {antecedent.Result}?"); }, TaskContinuationOptions.OnlyOnRanToCompletion);
ContinueWith的延续任务触发时,前值任务(antecedent)必然已经进入最终完成状态,再加了你指定了TaskContinuationOptions.OnlyOnRanToCompletion,只有前值任务执行成功才会运行这段逻辑,这时候调用antecedent.Result只是读取已经计算好的结果,不会有任何阻塞,也没有死锁风险。
这种场景下用Result和await antecedent唯一的差异只有异常处理逻辑:
- 如果前值任务执行失败,调用
Result会把异常包裹在AggregateException中抛出,需要手动解包才能拿到原始异常 await失败的任务会直接抛出原始异常,不需要额外解包
你的示例里因为加了执行成功的前置约束,所以两种写法没有可感知的区别,用Result甚至还能省掉await生成状态机的微小开销。
其他常见的合法使用场景
- 已经提前调用过
Task.Wait()、Task.WaitAll()等方法确认任务完成后,读取结果 - 读取
Task.FromResult构造的已完成任务的结果 - 没有同步上下文的环境(比如控制台程序的同步Main方法)中,调用生命周期极短的本地任务的Result,这种场景几乎不会触发死锁(不过还是更推荐用异步Main入口)
不要用Result的核心边界
只要你不能100%确定调用时任务已经完成,就绝对不要用Result:如果任务还在运行,会直接阻塞当前线程,要是当前线程存在同步上下文(比如WinForm/WPF的UI线程、ASP.NET Framework的请求线程),大概率会触发死锁:异步方法的后续执行需要等待当前线程释放,当前线程又在等任务执行完成,直接卡死。
内容的提问来源于stack exchange,提问作者causeofhell
相关产品推荐
相关产品推荐

