在什么场景下想要使用
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));
}
Foo()
将变为 FooAsync()
。
Thread.Sleep
用于您的任务,而应改为 await Task.Delay(num)
Task.Delay
不是 Task.AsyncDelay
,因为任务上的所有方法都是异步的
async void
而不是 async Task
。该方法崩溃是因为它使用了一个声明为控制器成员的实体框架上下文对象,该对象在方法完成执行之前被释放。框架在其方法完成执行之前就将控制器配置好了。我将方法更改为 async Task 并且它有效。
通常,您会想要返回一个任务。主要的例外应该是当您需要具有 void 返回类型(用于事件)时。如果没有理由不允许调用者等待您的任务,为什么不允许呢?返回 void 的异步方法在另一个方面是特殊的:它们代表顶级异步操作,并且当您的任务返回异常时,它们具有额外的规则。最简单的方法是通过一个示例来显示差异:
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));
}
是的,在这里使用 async
和 await
,它们可以确保您的方法在抛出异常时仍然正常工作。
我看到了这篇由 Jérôme Laban 撰写的关于 async
和 void
的非常有用的文章:https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.html
最重要的是,async+void
可能会使系统崩溃,并且通常只能在 UI 端事件处理程序中使用。
这背后的原因是 AsyncVoidMethodBuilder 使用的同步上下文,在这个例子中没有。当没有环境同步上下文时,任何由 async void 方法的主体未处理的异常都会在 ThreadPool 上重新引发。虽然似乎没有其他合乎逻辑的地方可以引发这种未处理的异常,但不幸的是进程正在被终止,因为 ThreadPool 上的未处理异常有效地终止了自 .NET 2.0 以来的进程。您可以使用 AppDomain.UnhandledException 事件拦截所有未处理的异常,但无法从该事件中恢复进程。在编写 UI 事件处理程序时,异步 void 方法在某种程度上是无痛的,因为异常的处理方式与非异步方法中的处理方式相同;它们被扔到 Dispatcher 上。有可能从此类异常中恢复,对于大多数情况来说是正确的。然而,在 UI 事件处理程序之外,异步 void 方法使用起来有些危险,而且可能不容易找到。
调用 async void 的问题在于
你甚至没有拿回任务。您无法知道函数的任务何时完成。 —— async 和 await 速成课程 |旧事新事
以下是调用异步函数的三种方法:
异步任务
我认为您也可以使用 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());
}
}
}
According to Microsoft documentation,切勿使用 async void
不要这样做:以下示例使用 async void 来完成第一次等待时的 HTTP 请求:这在 ASP.NET Core 应用程序中始终是一种不好的做法。在 HTTP 请求完成后访问 HttpResponse。使进程崩溃。
我的回答很简单,你不能等待 void 方法
Error CS4008 Cannot await 'void' TestAsync e:\test\TestAsync\TestAsyncProgram.cs
所以如果方法是async
,最好是可等待的,因为你会失去async
的优势。
f
而不是g
。f
的异常被传递给SynchronizationContext
。g
将引发UnobservedTaskException
,但如果未处理,UTE
将不再使进程崩溃。在某些情况下,像这样被忽略的“异步异常”是可以接受的。Task
导致异常的WhenAny
。你通常只需要处理第一个,而你经常想忽略其他的。WhenAny
的原因是否可以忽略其他异常:我拥有的主要用例仍然最终等待剩余的任务当任何完成时,无论有无例外。