asynchronous/等待 – 什么时候返回一个任务与无效?

在什么情况下会想要使用

public async Task AsyncMethod(int num) 

代替

 public async void AsyncMethod(int num) 

我能想到的唯一情况是如果你需要任务来跟踪它的进度。

另外,在下面的方法中,async和await关键字是不必要的?

  public static async void AsyncMethod2(int num) { await Task.Factory.StartNew(() => Thread.Sleep(num)); } 

1)通常,你会想要返回一个Task 。 主要的exception应该是当你需要有一个void返回types(事件)。 如果没有理由不允许主叫方await你的任务,为什么不允许呢?

2)返回void async方法在另一个方面是特殊的:它们表示顶级的asynchronous操作 ,并且当你的任务返回一个exception时有额外的规则。 最简单的方法是用一个例子来说明差异:

 static async void f() { await h(); } static async Task g() { await h(); } static async Task h() { throw new NotImplementedException(); } private void button1_Click(object sender, EventArgs e) { f(); } private void button2_Click(object sender, EventArgs e) { g(); } private void button3_Click(object sender, EventArgs e) { GC.Collect(); } 

f的例外总是“被观察”。 留下顶级asynchronous方法的exception就像其他任何未处理的exception一样。 g的例外从未被观察到。 当垃圾收集器清理任务时,它看到任务导致exception,没有人处理exception。 发生这种情况时, TaskScheduler.UnobservedTaskException处理程序将运行。 你不应该让这种事情发生。 要使用你的例子,

 public static async void AsyncMethod2(int num) { await Task.Factory.StartNew(() => Thread.Sleep(num)); } 

是的,使用asyncawait这里await ,他们确保你的方法仍然正常工作,如果抛出exception。

有关更多信息,请参阅: http : //msdn.microsoft.com/en-us/magazine/jj991977.aspx

我遇到了这个有关async所写的asyncvoid非常有用的文章: http : //www.jaylee.org/post/2012/07/08/c-sharp-async-tips-and-tricks-part-2 -async-void.aspx

底线是一个async+void可以使系统崩溃,通常只能在UI侧事件处理程序中使用。

其原因是由AsyncVoidMethodBuilder使用的同步上下文,在这个例子中没有。 当没有环境的同步上下文时,asynchronousvoid方法的主体未处理的任何exception在ThreadPool上重新运行。 虽然似乎没有其他合乎逻辑的地方可以抛出这种未处理的exception,但不幸的结果是进程正在终止,因为ThreadPool的未处理exception有效地终止了自.NET 2.0以来的进程。 您可以使用AppDomain.UnhandledException事件拦截所有未处理的exception,但是无法从此事件恢复进程。

在编写UI事件处理程序时,asynchronous无效方法在某种程度上是无痛的,因为exception处理方式与非asynchronous方法中的处理方式相同; 他们被扔在调度员。 有可能从这种例外中恢复过来,对于大多数情况来说,这是不正确的。 然而,在UI事件处理程序之外,asynchronous无效方法使用起来有些危险,可能并不容易find。

我从这个声明中得到了清晰的想法。

  1. asynchronous无效方法具有不同的error handling语义。 当exception被抛出asynchronous任务或asynchronous任务方法时,该exception被捕获并放置在任务对象上。 使用async void方法,不存在任何Task对象,因此asynchronousvoid方法中抛出的任何exception将直接在SynchronizationContext(SynchronizationContext表示可能执行的代码位置“where”代码)上引发,而asynchronousvoid方法开始

asynchronous无效方法的exception不能通过Catch抓住

 private async void ThrowExceptionAsync() { throw new InvalidOperationException(); } public void AsyncVoidExceptions_CannotBeCaughtByCatch() { try { ThrowExceptionAsync(); } catch (Exception) { // The exception is never caught here! throw; } } 

可以使用AppDomain.UnhandledException或GUI / ASP.NET应用程序的类似catch-all事件来观察这些exception,但将这些事件用于常规exception处理是不可维护的(它会使应用程序崩溃)。

  1. asynchronous无效方法具有不同的组成语义。 asynchronous方法返回任务或任务可以很容易地使用await,Task.WhenAny,Task.WhenAll等。 返回void的asynchronous方法不提供一个简单的方法来通知他们已经完成的调用代码。 启动几个asynchronous无效方法很容易,但是确定何时完成并不容易。 Async void方法在启动和完成时会通知它们的SynchronizationContext,但是对于常规的应用程序代码,自定义的SynchronizationContext是一个复杂的解决scheme。

  2. Async Void方法在使用同步事件处理程序时很有用,因为它们直接在SynchronizationContext上引发exception,这与同步事件处理程序的行为方式类似

有关更多详细信息,请查看此链接https://msdn.microsoft.com/zh-cn/magazine/jj991977.aspx