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));
}
485844 次浏览
  1. 通常,您希望返回Task。主要的例外应该是当你需要有一个void返回类型(事件)。如果没有理由不允许调用者await执行任务,为什么不允许呢?

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

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的例外总是“被观察到”。留下顶级异步方法的异常被简单地视为任何其他未处理的异常。g的异常从未被观察到。当垃圾收集器来清理任务时,它看到该任务导致了一个异常,并且没有人处理该异常。当这种情况发生时,TaskScheduler.UnobservedTaskException处理程序将运行。你不应该让这种事情发生。用你的例子来说,

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

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

更多信息请参见:https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming

我看到了Jérôme Laban写的关于asyncvoid的非常有用的文章: # EYZ0 < / p >

底线是async+void可能导致系统崩溃,通常只应该在UI侧事件处理程序上使用。

这背后的原因是同步上下文使用 AsyncVoidMethodBuilder,在本例中为none。当没有 环境中未处理的任何异常 async void方法的主体在ThreadPool上被重新抛出。而 似乎没有其他逻辑上的地方可以处理这种情况 异常可能被抛出,不幸的后果是该进程 正在被终止,因为线程池上未处理的异常 有效地终止自。net 2.0以来的进程。你可以拦截 所有未处理的异常使用AppDomain。UnhandledException事件, 但是没有办法从这个事件中恢复进程 当编写UI事件处理程序时,async void方法在某种程度上是 无痛是因为异常的处理方式与 non-async方法;它们被扔到Dispatcher上。有一个 从这种例外情况中恢复的可能性是正确的 在大多数情况下。然而,在UI事件处理程序之外,async无效

.

.

.

.

我认为您也可以使用async void来启动后台操作,只要您注意捕获异常。想法吗?

class Program {


static bool isFinished = false;


static void Main(string[] args) {


// Kick off the background operation and don't care about when it completes
BackgroundWork();


Console.WriteLine("Press enter when you're ready to stop the background operation.");
Console.ReadLine();
isFinished = true;
}


// Using async void to kickoff a background operation that nobody wants to be notified about when it completes.
static async void BackgroundWork() {
// It's important to catch exceptions so we don't crash the appliation.
try {
// This operation will end after ten interations or when the app closes. Whichever happens first.
for (var count = 1; count <= 10 && !isFinished; count++) {
await Task.Delay(1000);
Console.WriteLine($"{count} seconds of work elapsed.");
}
Console.WriteLine("Background operation came to an end.");
} catch (Exception x) {
Console.WriteLine("Caught exception:");
Console.WriteLine(x.ToString());
}
}
}

调用async void的问题是

你连任务都拿不回来。您无法知道函数的任务何时完成。——# EYZ0

下面是调用async函数的三种方法:

async Task<T> SomethingAsync() { ... return t; }
async Task SomethingAsync() { ... }
async void SomethingAsync() { ... }

在所有情况下,功能都转换为一连串的任务。区别在于函数返回什么。

在第一种情况下,函数返回一个最终生成t的任务。

在第二种情况下,函数返回一个没有product的任务,但是你可以 仍然在等待它知道它什么时候运行到完成 第三种情况比较糟糕。第三种情况和第二种情况一样,除了 你连任务都拿不回来。你根本不知道什么时候

.函数任务已经完成 async void case是一个"fire and forget":你启动了任务链,但你不关心它是什么时候开始的 完成了。当函数返回时,你所知道的就是 到第一个await执行为止。凡事先等而后行 会在未来某个不确定的时间运行,而你没有 访问权。< / p >
我的答案很简单 您不能等待无效方法

Error   CS4008  Cannot await 'void' TestAsync   e:\test\TestAsync\TestAsyncProgram.cs

因此,如果方法是async,最好是可等待的,因为您可能会失去async的优势。