await Task<T> 和 Task<T>.Result 有啥区别?

Posted

技术标签:

【中文标题】await Task<T> 和 Task<T>.Result 有啥区别?【英文标题】:What is the difference between await Task<T> and Task<T>.Result?await Task<T> 和 Task<T>.Result 有什么区别? 【发布时间】:2015-02-12 09:11:12 【问题描述】:
public async Task<string> GetName(int id)

    Task<string> nameTask = Task.Factory.StartNew(() => string.Format("Name matching id 0 = Developer", id));
    return nameTask.Result;

在上面的方法返回语句中,我使用了Task&lt;T&gt;.Result 属性。

public async Task<string> GetName(int id)

     Task<string> nameTask = Task.Factory.StartNew(() => string.Format("Name matching id 0 = Developer", id));
     return await nameTask;

我在这里使用await Task&lt;T&gt;。如果我认为 await 会释放调用线程但Task&lt;T&gt;.Result 会阻塞它,我不会错,对吗?

【问题讨论】:

由于第二个代码没有延续 - 你什么也得不到。在第一个代码中,您只需将方法标记为异步,但您不等待。 要了解有关async-await 的更多信息,请查看my async-awaitcuration 上的文章。 @RoyiNamir,你的说法不正确吗?第一种方法通过使用async 没有任何收获,因为该方法阻塞了调用线程。但是,第二种方法应该让出当前线程并且在任务设置为完成状态之前不会阻塞。 @MattWolf - 我同意第二种方法对调用它的任何方法都有好处,通过在任务完成时让它将控制权交还给调用者。 【参考方案1】:

如果我认为 await 会释放调用线程但 Task.Result 会阻塞它,我不会错,对吗?

你是对的,只要任务没有同步完成。如果是这样,使用Task.Resultawait task 将同步执行,因为await 将首先检查任务是否已完成。否则,如果任务没有完成,它将阻塞Task.Result的调用线程,而使用awaita同步等待任务完成。另一个不同的是异常处理。前者将传播AggregationException(可能包含一个或多个异常),而后者将解包并返回底层异常。

附带说明,using asynchronous wrappers over sync methods is bad practice and should be avoided. 此外,在异步方法中使用 Task.Result 会导致死锁,也应避免。

【讨论】:

这种“在异步方法中使用Task.Result会导致死锁”怎么会发生死锁,你能详细说明一下吗?【参考方案2】:

如果我认为 await 会释放调用线程但 Task.Result 会阻塞它,我不会错,对吗?

一般来说,是的。 await task; 将“让出”当前线程。 task.Result 将阻塞当前线程。 await 是异步等待; Result 是阻塞等待。

还有另一个更小的区别:如果任务在错误状态下完成(即,出现异常),则await 将(重新)按原样引发该异常,但Result 会将异常包装在一个AggregateException

附带说明,请避免使用Task.Factory.StartNew。它几乎从来都不是正确的使用方法。如果您需要在后台线程上执行工作,请首选Task.Run

ResultStartNew 都适用,如果你正在做 dynamic task parallelism;否则,应避免使用它们。如果您正在使用asynchronous programming,则两者都不合适。

【讨论】:

感谢 Stephen Cleary 和 Yuval Itzchakov 提供更多相关知识。 await Task.WhenAll(task1, task2)之后得到结果有什么不同吗? await task1task1.Result?等待WhenAll 应该已经用线程完成了所有工作,对吧? @demo:我更喜欢 await 有两个原因:1)它避免了 AggregateException 包装器,以及 2)它对代码更改更加宽容 - 即,如果有人更早地更改方法,现在任务不再完成。

以上是关于await Task<T> 和 Task<T>.Result 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用 IAsyncEnumerable 比返回 async/await Task<T> 慢?

深入了解异步async/await,为啥这种异步的性能这么高?异步的原理,本文彻底来个说明

c# await 不能作用于 async上?

异步方法中的 Await vs Task.Result [重复]

await Task.Yield(); 超简单理解!

C#5 异步返回类型 - 澄清?