异步/等待的限制

4
如果有的话,那么在使用带有方法签名的async和在这些方法中使用表达式的await时,还有哪些限制?目前我知道一个被标记为async的方法只能具有以下一个返回类型(也许还有更多?)。
  • void
  • Task
  • Task<TResult>

请注意,本文中的html标签已保留。

1
应用await运算符的任务通常是从调用实现了基于任务的异步模式的方法返回的值。例如,Task或Task<TResult>类型的值。 - gdoron
@gdoron 比之前的评论好多了。 - Ondrej Janacek
真正的答案并不是没有其他限制,甚至更少。 :) - gdoron
3个回答

7

我在我的博客上有一篇介绍,你可能会发现它很有帮助。

await只需要传递一个“可等待”的实例(与foreach类似的匹配某种模式)。最常见的可等待对象是任务,但还有其他类型(例如Windows Store异步操作)。

async必须用于使用await的任何方法。 async方法必须返回TaskTask<T>,或者如果你非常确定,可以返回void


6
async修饰符仅允许用于返回类型为voidTaskTask<T>的方法上。一般来说,它应该只用于包含await运算符的方法上。 await运算符可用于任何类型t,其中(来源于C#语言规范,7.7.7): 有一个名为GetAwaiter的可访问的实例或扩展方法,没有参数和类型参数,并且返回类型A,对于该类型,所有以下条件都成立:
  • A实现接口System.Runtime.CompilerServices.INotifyCompletion
  • A具有可访问的、可读的IsCompleted属性,类型为bool
  • A具有一个无参数且没有类型参数的可访问实例方法GetResult
在实践中,这通常意味着您可以在框架中的任何TaskTask<T>IAsyncOperationIAsyncOperation<T>(来自Windows Store API)和其他几个特定类型上使用await

谢谢,这正是我在寻找的... 我在 @jonskeet 的演示代码中看到了这个。 - Ivandro Jao

3
你遇到了异步方法的主要技术限制。我可以提供以下额外的指导:
  1. 声明一个没有至少一个操作awaitasync方法是毫无意义的,因为这样的方法等同于常规的同步方法。
  2. 应避免编写async void方法。这些方法仅用于支持编写异步事件处理程序。
  3. 如果你调用一个async方法,则应该await它(或者至少访问Task的IsFaulted状态并检索其Exception(如果发生异常))。如果你不这样做,并且该方法内的任何等待操作引发你未捕获的异常,则该异常将在终结器线程上重新抛出1(很糟糕)。

你可以await任何具有可等待类型的对象,即声明了成员的类型:

TAwaiter GetAwaiter();

在这里,TAwaiter是一个类型,声明了以下成员:

bool IsCompleted { get; }
TResult GetResult();
void OnCompleted(Action continuation);  // inherited from INotifyCompletion
IsCompleted属性的含义很明显:如果可等待的操作已经完成,则返回true;否则返回false
GetResult()方法返回操作的结果,或者如果操作没有返回类型,则返回void。如果在IsCompletedfalse时调用此方法,则应抛出异常。
OnCompleted()方法用于注册一个后续操作,即当操作完成后调用的代码。这个成员实际上应该从System.Runtime.CompilerServices命名空间中继承INotifyCompletion接口(所有的awaiter都应该实现此接口)。

1.NET 4.5中不再是默认行为,但仍可通过配置更改在客户端机器上启用。

除了(3)之外,答案很好。默认情况下,在线程池或终结器线程上不再引发未观察到的任务异常(自.NET 4.5起)。 - Stephen Cleary
@StephenCleary 添加了一个脚注。即使在.NET 4.5中它不是默认行为,但仍然可以在用户的机器配置(或应用程序配置)中启用。由于影响是应用程序范围内的,最好不要假设默认行为。 - Mike Strobel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接