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