提问者:小点点

Async/Await-何时返回任务vs void?


在什么情况下会使用

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));
}

共3个答案

匿名用户

1)通常情况下,您希望返回。主要的例外应该是当您需要具有返回类型(用于事件)时。如果没有理由不允许调用方具有您的任务,为什么不允许它呢?

2)返回的方法在另一个方面是特殊的:它们表示顶级异步操作,并且在任务返回异常时有额外的规则发挥作用。最简单的方法是用一个示例来显示区别:

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();
}

处理程序将运行。你永远不应该让这种事发生。要使用您的示例,

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

是的,在这里使用,它们确保如果抛出异常,您的方法仍然正确工作。

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

匿名用户

我看到了这篇由JRME Laban编写的关于的非常有用的文章:https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.html

底线是可能使系统崩溃,通常只应在UI端事件处理程序上使用。

这背后的原因是AsyncVoidMethodBuilder使用的同步上下文,在本例中为none。当没有环境同步上下文时,异步void方法主体未处理的任何异常都将在线程池上重新抛出。虽然似乎没有其他合乎逻辑的地方可以抛出这种未经处理的异常,但不幸的结果是进程被终止,因为线程池上未经处理的异常自。NET2.0以来有效地终止了进程。您可以使用AppDomain.UnhandleDexception事件截获所有未处理的异常,但无法从此事件恢复进程。

在编写UI事件处理程序时,异步void方法在某种程度上并不痛苦,因为异常的处理方式与非异步方法相同;它们被扔到调度员身上。有可能从这些例外中恢复过来,在大多数情况下是完全正确的。但是,在UI事件处理程序之外,使用异步void方法是危险的,而且可能不太容易找到。

匿名用户

我从这些陈述中清楚地知道了。

不能用Catch捕获来自异步Void方法的异常

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事件来观察,但是使用这些事件进行常规异常处理会导致不可维护(它会使应用程序崩溃)。

异步void方法具有不同的组合语义。返回Task或Task的异步方法可以使用await,Task.WhenAny,Task.WhenAll等轻松组合。返回void的异步方法并不提供一种简单的方法来通知调用代码它们已经完成。启动几个异步void方法很容易,但确定它们何时完成并不容易。异步void方法将在它们开始和完成时通知它们的SynchronizationContext,但是自定义SynchronizationContext对于常规应用程序代码来说是一个复杂的解决方案。

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

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