在查看各种C#异步CTP示例时,我看到一些返回void
的异步函数,以及其他返回非泛型Task
的函数。我可以看到为什么返回Task<MyType>
对于异步操作完成时将数据返回给调用者很有用,但是我看到的返回类型为Task
的函数永远不会返回任何数据。为什么不返回void
?
答案 0 :(得分:199)
SLaks和Killercam的答案很好;我以为我只是添加了一些上下文。
您的第一个问题主要是关于可以标记哪些方法async
。
标记为
async
的方法可以返回void
,Task
或Task<T>
。它们之间有什么区别?
可以等待Task<T>
返回异步方法,当任务完成时,它将提供一个T.
可以等待Task
返回异步方法,当任务完成时,计划继续执行任务。
无法等待void
返回异步方法;这是一种“火与死”的方法。它确实异步工作,你无法告诉它什么时候完成。这有点奇怪;正如SLaks所说,通常你只会在制作异步事件处理程序时这样做。事件触发,处理程序执行;没有人会“等待”事件处理程序返回的任务,因为事件处理程序不返回任务,即使他们这样做了,什么代码会使用Task来做什么?通常不是用户代码首先将控制转移到处理程序。
您的第二个问题,在评论中,主要是关于await
编辑的内容:
await
可以使用哪种方法?空洞返回方法可以await
编辑吗?
不,无法等待返回空洞的方法。编译器将await M()
转换为对M().GetAwaiter()
的调用,其中GetAwaiter
可能是实例方法或扩展方法。等待的价值必须是你可以得到一个等待者的价值;显然,返回空隙的方法不会产生一个可以获得等待的值。
Task
- 返回方法可以产生等价值。我们预计第三方将希望创建自己的Task
实现 - 类似于可以等待的对象,您将能够等待它们。但是,除了async
,void
或Task
之外,您不会声明Task<T>
方法返回任何内容。
(更新:我的最后一句话可能会被未来的C#版本伪造;有一项建议允许异步方法的任务类型以外的返回类型。)
(更新:上面提到的功能进入了C#7。)
答案 1 :(得分:22)
如果调用者想要等待任务或添加延续。
事实上,返回void
的唯一原因是,如果无法返回Task
,因为您正在编写事件处理程序。
答案 2 :(得分:18)
返回Task
和Task<T>
的方法是可组合的 - 这意味着您可以在await
方法中async
使用它们。
async
的 void
方法不可组合,但它们还有其他两个重要属性:
当您处理维护未完成异步操作的计数的上下文时,第二点非常重要。
ASP.NET上下文就是这样一个上下文;如果您使用异步Task
方法而不等待异步void
方法,那么ASP.NET请求将过早完成。
另一个上下文是我为单元测试编写的AsyncContext
(可用here) - AsyncContext.Run
方法跟踪未完成的操作计数,并在它为零时返回。
答案 3 :(得分:12)
类型Task<T>
是任务并行库(TPL)的主力类型,它表示“将来会产生T
类型结果的某些工作/作业”的概念。 “通用将在未来完成但不返回结果”的概念由非通用任务类型表示。
确切地说,如何生成类型T
的结果是特定任务的实现细节;可能会将工作转移到本地计算机上的另一个进程,另一个线程等.TPL任务通常从当前进程中的线程池中移植到工作线程,但该实现细节不是{{1 }};相反,Task<T>
可以表示产生Task<T>
的任何高延迟操作。
根据您的上述评论:
T
表达式意味着“计算此表达式以获取表示将来生成结果的工作的对象。将当前方法的其余部分注册为与该任务的延续相关联的回调。生成该任务并注册回叫,立即将控制权返回给我的呼叫者“。这与常规方法调用相反/相反,这意味着“记住你正在做的事情,运行这个方法直到它完全完成然后从你离开的地方开始,现在知道方法的结果”。
编辑:我应该引用Eric Lippert在2011年10月的MSDN杂志上的文章,因为这对我理解这些东西起了很大帮助。
对于负载更多的信息和白页,请参阅here。
我希望这会有所帮助。