137

在查看各种 C# Async CTP 示例时,我看到一些返回void的异步函数,以及返回非泛型的其他函数Task。我可以看到为什么Task<MyType>在异步操作完成时返回 a 对于将数据返回给调用者很有用,但是我见过的返回类型为Task从不返回任何数据的函数。为什么不回来void

4

4 回答 4

219

SLaks 和 Killercam 的回答很好;我想我会添加更多的上下文。

您的第一个问题本质上是关于可以标记哪些方法async

标记为的方法async可以返回void,TaskTask<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。)

于 2011-11-07T22:57:49.730 回答
24

如果调用者想要等待任务或添加延续。

实际上,返回的唯一原因void是如果您因为正在编写事件处理程序而无法返回。Task

于 2011-11-07T22:09:42.427 回答
19

方法返回Task并且Task<T>是可组合的 - 这意味着您可以在方法await内部使用它们async

async返回的方法void是不可组合的,但它们确实有另外两个重要的属性:

  1. 它们可以用作事件处理程序。
  2. 它们代表“顶级”异步操作。

当您处理维护未完成异步操作计数的上下文时,第二点很重要。

ASP.NET 上下文就是这样一种上下文。如果您使用异步Task方法而不从异步方法中等待它们void,那么 ASP.NET 请求将过早完成。

另一个上下文是AsyncContext我为单元测试编写的(可在此处获得) - 该AsyncContext.Run方法跟踪未完成的操作计数并在它为零时返回。

于 2011-11-09T01:46:07.680 回答
12

类型Task<T>是任务并行库(TPL)的主力类型,它代表了“某些工作/工作将T在未来产生类型结果”的概念。“将在未来完成但不返回结果的工作”的概念由非泛型 Task 类型表示。

确切地说,类型的结果T将如何产生以及特定任务的实现细节;工作可能被外包给本地机器上的另一个进程,另一个线程等。TPL 任务通常从当前进程的线程池中被外包给工作线程,但实现细节不是该Task<T>类型的基础;而是 aTask<T>可以表示任何产生 a 的高延迟操作T

根据您上面的评论:

await表达式的意思是“评估此表达式以获得一个对象,该对象表示将来会产生结果的工作。将当前方法的其余部分注册为与该任务的继续相关联的回调。一旦产生该任务并回调已注册,立即将控制权交还给我的调用者”。这与常规方法调用相反/相反,这意味着“记住你在做什么,运行这个方法直到它完全完成,然后从你离开的地方开始,现在知道方法的结果”。


编辑:我应该在 2011 年 10 月的 MSDN 杂志上引用 Eric Lippert 的文章,因为这对我理解这些东西有很大帮助。

有关加载更多信息和白页,请参见此处

我希望这会有所帮助。

于 2011-11-07T22:33:42.640 回答