C# async/await - 何时返回任务 vs 无效?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12144077/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
async/await - when to return a Task vs void?
提问by user981225
Under what scenarios would one want to use
在什么场景下想要使用
public async Task AsyncMethod(int num)
instead of
代替
public async void AsyncMethod(int num)
The only scenario that I can think of is if you need the task to be able to track its progress.
我能想到的唯一情况是,如果您需要任务能够跟踪其进度。
Additionally, in the following method, are the async and await keywords unnecessary?
另外,在下面的方法中, async 和 await 关键字是不必要的吗?
public static async void AsyncMethod2(int num)
{
await Task.Factory.StartNew(() => Thread.Sleep(num));
}
采纳答案by user981225
1) Normally, you would want to return a Task. The main exception should be when you needto have a voidreturn type (for events). If there's no reason to disallow having the caller awaityour task, why disallow it?
1) 通常,您会想要返回一个Task. 主要的例外应该是当你需要有一个void返回类型(事件)。如果没有理由禁止调用者执行await您的任务,为什么要禁止它?
2) asyncmethods that return voidare special in another aspect: they represent top-level async operations, and have additional rules that come into play when your task returns an exception. The easiest way is to show the difference is with an example:
2)async返回的方法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's exception is always "observed". An exception that leaves a top-level asynchronous method is simply treated like any other unhandled exception. g's exception is never observed. When the garbage collector comes to clean up the task, it sees that the task resulted in an exception, and nobody handled the exception. When that happens, the TaskScheduler.UnobservedTaskExceptionhandler runs. You should never let this happen. To use your example,
f的例外总是“观察到”。离开顶级异步方法的异常被简单地视为任何其他未处理的异常。g的异常从未被观察到。当垃圾收集器来清理任务时,它看到任务导致异常,并且没有人处理异常。发生这种情况时,TaskScheduler.UnobservedTaskException处理程序运行。你永远不应该让这种情况发生。要使用您的示例,
public static async void AsyncMethod2(int num)
{
await Task.Factory.StartNew(() => Thread.Sleep(num));
}
Yes, use asyncand awaithere, they make sure your method still works correctly if an exception is thrown.
是的,使用async,await在这里,如果抛出异常,它们可以确保您的方法仍然正常工作。
for more information see: http://msdn.microsoft.com/en-us/magazine/jj991977.aspx
有关更多信息,请参阅:http: //msdn.microsoft.com/en-us/magazine/jj991977.aspx
回答by Davide Icardi
I have come across this very usefull article about asyncand voidwritten by Jér?me Laban:
https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.html
我也碰到过这种关于非常有用的文章async和void?,由JER写我拉班:
https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async -void.html
The bottom line is that an async+voidcan crash the system and usually should be used only on the UI side event handlers.
最重要的是,它async+void可能会导致系统崩溃,通常只应在 UI 侧事件处理程序上使用。
The reason behind this is the Synchronization Context used by the AsyncVoidMethodBuilder, being none in this example. When there is no ambient Synchronization Context, any exception that is unhandled by the body of an async void method is rethrown on the ThreadPool. While there is seemingly no other logical place where that kind of unhandled exception could be thrown, the unfortunate effect is that the process is being terminated, because unhandled exceptions on the ThreadPool effectively terminate the process since .NET 2.0. You may intercept all unhandled exception using the AppDomain.UnhandledException event, but there is no way to recover the process from this event.
When writing UI event handlers, async void methods are somehow painless because exceptions are treated the same way found in non-async methods; they are thrown on the Dispatcher. There is a possibility to recover from such exceptions, with is more than correct for most cases. Outside of UI event handlers however, async void methods are somehow dangerous to use and may not that easy to find.
这背后的原因是 AsyncVoidMethodBuilder 使用的同步上下文,在这个例子中没有。当没有环境同步上下文时,异步 void 方法主体未处理的任何异常都会在 ThreadPool 上重新抛出。虽然似乎没有其他合乎逻辑的地方可以抛出这种未处理的异常,但不幸的结果是进程被终止,因为自 .NET 2.0 以来,线程池上的未处理异常有效地终止了进程。您可以使用 AppDomain.UnhandledException 事件拦截所有未处理的异常,但无法从该事件中恢复进程。
在编写 UI 事件处理程序时,异步 void 方法在某种程度上是无痛的,因为异常的处理方式与非异步方法中的处理方式相同;它们被扔到调度器上。有可能从此类异常中恢复,在大多数情况下是正确的。然而,在 UI 事件处理程序之外,异步 void 方法使用起来有些危险,并且可能不容易找到。
回答by Nayas Subramanian
I got clear idea from this statements.
我从这些陈述中得到了清晰的想法。
- Async void methods have different error-handling semantics. When an exception is thrown out of an async Task or async Task method, that exception is captured and placed on the Task object. With async void methods, there is no Task object, so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext(SynchronizationContext represents a location "where" code might be executed. ) that was active when the async void method started
- 异步 void 方法具有不同的错误处理语义。当异步任务或异步任务方法抛出异常时,该异常将被捕获并放置在任务对象上。对于 async void 方法,没有 Task 对象,因此从 async void 方法抛出的任何异常都将直接在 SynchronizationContext 上引发(SynchronizationContext 表示可能会执行代码的“位置”。)开始
Exceptions from an Async Void Method Can't Be Caught with Catch
Catch 无法捕获来自异步 Void 方法的异常
private async void ThrowExceptionAsync()
{
throw new InvalidOperationException();
}
public void AsyncVoidExceptions_CannotBeCaughtByCatch()
{
try
{
ThrowExceptionAsync();
}
catch (Exception)
{
// The exception is never caught here!
throw;
}
}
These exceptions can be observed using AppDomain.UnhandledException or a similar catch-all event for GUI/ASP.NET applications, but using those events for regular exception handling is a recipe for unmaintainability(it crashes the application).
这些异常可以使用 AppDomain.UnhandledException 或 GUI/ASP.NET 应用程序的类似包罗万象的事件来观察,但使用这些事件进行常规异常处理是不可维护性的一个秘诀(它会使应用程序崩溃)。
Async void methods have different composing semantics. Async methods returning Task or Task can be easily composed using await, Task.WhenAny, Task.WhenAll and so on. Async methods returning void don't provide an easy way to notify the calling code that they've completed. It's easy to start several async void methods, but it's not easy to determine when they've finished. Async void methods will notify their SynchronizationContext when they start and finish, but a custom SynchronizationContext is a complex solution for regular application code.
Async Void method useful when using synchronous event handler because they raise their exceptions directly on the SynchronizationContext, which is similar to how synchronous event handlers behave
异步 void 方法具有不同的组合语义。可以使用 await、Task.WhenAny、Task.WhenAll 等轻松组合返回 Task 或 Task 的异步方法。返回 void 的异步方法没有提供一种简单的方法来通知调用代码它们已经完成。启动几个 async void 方法很容易,但很难确定它们何时完成。异步 void 方法将在开始和结束时通知它们的 SynchronizationContext,但自定义 SynchronizationContext 是常规应用程序代码的复杂解决方案。
异步 Void 方法在使用同步事件处理程序时很有用,因为它们直接在 SynchronizationContext 上引发异常,这类似于同步事件处理程序的行为方式
For more details check this link https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
有关更多详细信息,请查看此链接 https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
回答by bboyle1234
I think you can use async voidfor kicking off background operations as well, so long as you're careful to catch exceptions. Thoughts?
我认为您也可以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());
}
}
}
回答by user8128167
The problem with calling async void is that you don't even get the task back, you have no way of knowing when the function's task has completed (see https://blogs.msdn.microsoft.com/oldnewthing/20170720-00/?p=96655)
调用 async void 的问题是您甚至没有取回任务,您无法知道函数的任务何时完成(请参阅https://blogs.msdn.microsoft.com/oldnewthing/20170720-00/ ?p=96655)
Here are the three ways to call an async function:
以下是调用异步函数的三种方式:
async Task<T> SomethingAsync() { ... return t; }
async Task SomethingAsync() { ... }
async void SomethingAsync() { ... }
In all the cases, the function is transformed into a chain of tasks. The difference is what the function returns.
In the first case, the function returns a task that eventually produces the t.
In the second case, the function returns a task which has no product, but you can still await on it to know when it has run to completion.
The third case is the nasty one. The third case is like the second case, except that you don't even get the task back. You have no way of knowing when the function's task has completed.
The async void case is a "fire and forget": You start the task chain, but you don't care about when it's finished. When the function returns, all you know is that everything up to the first await has executed. Everything after the first await will run at some unspecified point in the future that you have no access to.
在所有情况下,该功能都被转换为一系列任务。不同之处在于函数返回的内容。
在第一种情况下,该函数返回一个最终产生 t 的任务。
在第二种情况下,该函数返回一个没有产品的任务,但您仍然可以等待它以了解它何时运行完成。
第三种情况是令人讨厌的。第三种情况与第二种情况类似,只是您甚至没有取回任务。您无法知道函数的任务何时完成。
async void 情况是“即发即忘”:您启动了任务链,但您并不关心它何时完成。当函数返回时,您所知道的就是直到第一个 await 为止的所有内容都已执行。第一次 await 之后的所有内容都将在未来某个您无法访问的未指定点运行。
回答by Serg Shevchenko
My answer is simple you can not await void method
我的回答很简单,你不能等待 void 方法
Error CS4008 Cannot await 'void' TestAsync e:\test\TestAsync\TestAsyncProgram.cs
So if the method is async it is better to be awaitable, because you can loose async advantage.
因此,如果该方法是异步的,最好是可等待的,因为您可能会失去异步优势。
回答by Robouste
According to Microsoft documentation, should NEVER use async void
根据 Microsoft 文档,永远不要使用async void
Do not do this: The following example uses
async voidwhich makes the HTTP request complete when the first await is reached:
Which is ALWAYS a bad practice in ASP.NET Core apps.
Accesses the HttpResponse after the HTTP request is complete.
Crashes the process.
不要这样做:以下示例使用
async voidwhich 在到达第一个 await 时使 HTTP 请求完成:
这在 ASP.NET Core 应用程序中总是一个不好的做法。
在 HTTP 请求完成后访问 HttpResponse。
使进程崩溃。

