返回void和返回任务有什么区别?

Jam*_*add 121 c# asynchronous return-type task-parallel-library async-ctp

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

Eri*_*ert 204

SLaks和Killercam的答案很好; 我以为我只是添加了一些上下文.

您的第一个问题主要是关于可以标记哪些方法async.

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

  • @JamesCadd:假设一些异步工作抛出异常.**谁捕获它?**启动异步任务的代码不再存在于堆栈中 - 它甚至可能不在同一个*线程*上 - 并且异常假设所有catch/finally块都在*上堆*.所以你会怎么做?我们将异常信息存储在Task中,以便您以后可以检查它.但是如果该方法返回void,那么用户代码就没有可用的任务.我们究竟是如何处理这种情况引起了一些争议,我现在不记得我们决定了什么. (10认同)
  • 我实际上在BUILD问过Stephen Toub的这个问题.在.NET 4.0中未观察到,一旦TPL检测到它们未被观察到,任务中未处理的异常最终会使进程崩溃.在4.5中,他们已经更改了默认行为,以便仍然可以通过TaskScheduler :: UnobservedTaskException事件报告未观察到的异常,但不会再使进程崩溃.如果您想要*旧的4.0行为,您可以选择<runtime> <ThrowUnobservedTaskExceptions enabled ="true"/> </ runtime>.最有可能的变化恰恰是为了支持无效异步方法的即发即弃. (8认同)
  • +1我认为唯一缺少的是在返回void的异步方法中如何处理异常的区别. (7认同)
  • `async void`方法在它们开始执行时处于活动状态的`SynchronizationContext`上引发它们的异常.这类似于(同步)事件处理程序的行为.@DrewMarsh:`UnobservedTaskException`和运行时设置仅适用于"fire and forget"async*Task*方法,而不适用于`async void`方法. (4认同)

SLa*_*aks 22

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

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

  • 不,你不能。如果该方法返回“void”,您将无法获取它生成的任务。(实际上,我不确定它是否会生成“Task”) (2认同)

Ste*_*ary 18

方法返回Task,并Task<T>是可组合的-这意味着你可以await他们的内部async方法.

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

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

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

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

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


Moo*_*ght 12

Type Task<T>是任务并行库(TPL)的主力类型,它代表了" T将来会产生类型结果的一些工作/工作"的概念."通用将在未来完成但不返回结果"的概念由非通用任务类型表示.

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

根据您的上述评论:

await表达的意思是"评估这个表达式,以获得表示将在未来产生结果的工作的对象.注册的当前方法为背面与该任务的延续相关联的呼叫的剩余部分.一旦任务被产生并回呼已注册,立即将控制权交还给我的来电者".这与常规方法调用相反/相反,这意味着"记住你正在做的事情,运行这个方法,直到它完全完成,然后从你离开的地方开始,现在知道方法的结果".


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

对于负载更多的信息和白页看到这里.

我希望这有一些帮助.