在什么情况下会想要使用
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
。主要的例外是你需要有一个void
返回类型(用于事件)。如果没有理由不允许调用者await
你的任务,为什么不允许它?
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
的例外总是“被观察”。离开顶级异步方法的异常被简单地视为任何其他未处理的异常。从未观察到g
的例外情况。当垃圾收集器来清理任务时,它会发现该任务导致异常,并且没有人处理该异常。当发生这种情况时,TaskScheduler.UnobservedTaskException
处理程序运行。你永远不应该让这件事发生。要使用你的例子,
public static async void AsyncMethod2(int num)
{
await Task.Factory.StartNew(() => Thread.Sleep(num));
}
是的,在这里使用async
和await
,如果抛出异常,它们会确保您的方法仍能正常工作。
有关更多信息,请参阅:http://msdn.microsoft.com/en-us/magazine/jj991977.aspx
我已经看过这篇关于由JérômeLaban撰写的关于qazxsw poi和async
的非常有用的文章:qazxsw poi
最重要的是,void
可能会使系统崩溃,并且通常只应在UI端事件处理程序上使用。
这背后的原因是AsyncVoidMethodBuilder使用的同步上下文,在此示例中为none。当没有环境同步上下文时,在ThreadPool上重新抛出异步void方法的主体未处理的任何异常。虽然似乎没有其他逻辑位置可以抛出那种未处理的异常,但不幸的结果是该进程被终止,因为ThreadPool上的未处理异常有效地终止了自.NET 2.0以来的进程。您可以使用AppDomain.UnhandledException事件拦截所有未处理的异常,但无法从此事件中恢复该进程。
在编写UI事件处理程序时,异步void方法在某种程度上是无痛的,因为异常的处理方式与非异步方法相同;他们被扔在Dispatcher上。有可能从这些例外中恢复,对大多数情况来说都是正确的。但是,在UI事件处理程序之外,异步void方法在某种程度上是危险的,并且可能不容易找到。
我从这些陈述中得到了明确的想法。
Async Void方法的例外情况无法通过Catch捕获
https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.html
对于GUI / ASP.NET应用程序,可以使用AppDomain.UnhandledException或类似的catch-all事件来观察这些异常,但是使用这些事件进行常规异常处理是不可维护性的一个因素(它会使应用程序崩溃)。
有关更多详细信息,请查看此链接async+void
调用异步void的问题是你甚至没有得到任务,你无法知道函数的任务何时完成(参见private async void ThrowExceptionAsync()
{
throw new InvalidOperationException();
}
public void AsyncVoidExceptions_CannotBeCaughtByCatch()
{
try
{
ThrowExceptionAsync();
}
catch (Exception)
{
// The exception is never caught here!
throw;
}
}
)
以下是调用异步函数的三种方法:
https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
在所有情况下,该功能都转换为一系列任务。不同之处在于函数返回的内容。
在第一种情况下,函数返回最终产生t的任务。
在第二种情况下,该函数返回一个没有产品的任务,但您仍然可以等待它知道它何时运行完成。
第三种情况是令人讨厌的。第三种情况就像第二种情况,除了你甚至没有得到任务。您无法知道函数的任务何时完成。
async void case是一个“一劳永逸”:你启动任务链,但你不关心它什么时候结束。当函数返回时,你所知道的是第一个await的所有内容都已执行。第一次等待之后的所有内容将在未来的某个未指定的点上运行,您无权访问。
我认为你也可以使用https://blogs.msdn.microsoft.com/oldnewthing/20170720-00/?p=96655开始后台操作,只要你小心捕捉异常。思考?
async Task<T> SomethingAsync() { ... return t; }
async Task SomethingAsync() { ... }
async void SomethingAsync() { ... }
我的回答很简单,你可以等待void方法错误CS4008无法等待'void'TestAsync e:\ test \ TestAsync \ TestAsync \ Program.cs
因此,如果方法是异步的,那么最好等待,因为你可以放松异步优势。