在什么情况下会想要使用
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));
}
答案 0 :(得分:355)
1)通常情况下,您需要返回Task
。当需要具有void
返回类型(对于事件)时,主要的例外情况应该是。如果没有理由拒绝让调用者await
成为你的任务,为什么不允许呢?
2)返回async
的{{1}}方法在另一个方面很特殊:它们代表顶级异步操作,并且具有在您的任务中发挥作用的其他规则返回异常。最简单的方法是通过示例显示差异:
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
,如果抛出异常,它们会确保您的方法仍能正常工作。
有关详细信息,请参阅:http://msdn.microsoft.com/en-us/magazine/jj991977.aspx
答案 1 :(得分:36)
我在JérômeLaban写的关于async
和void
的非常有用的文章中遇到过:
https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.html
底线是async+void
可能导致系统崩溃,通常只应在UI端事件处理程序上使用。
这背后的原因是使用的同步上下文 AsyncVoidMethodBuilder,在此示例中为none。什么时候没有 环境同步上下文,任何未处理的异常 在ThreadPool上重新抛出异步void方法的主体。而 似乎没有其他合乎逻辑的地方那种未经处理的地方 异常可能被抛出,不幸的结果就是这个过程 正在被终止,因为ThreadPool上的未处理异常 从.NET 2.0开始有效地终止进程。你可以拦截 使用AppDomain.UnhandledException事件的所有未处理的异常, 但是没有办法从这个事件中恢复过程。
在编写UI事件处理程序时,async void方法是以某种方式 无痛,因为异常的处理方式与之相同 非异步方法;他们被扔在Dispatcher上。有一个 从这些例外中恢复的可能性,不仅仅是正确的 对于大多数情况。但是,在UI事件处理程序之外,async void 方法在某种程度上是危险的使用,可能不容易找到。
答案 2 :(得分:24)
我从这些陈述中得到了明确的想法。
异步无效方法的异常无法通过Catch捕获
with open('file.txt', 'r') as f:
with open('file2.txt', 'w') as w:
data = f.readlines()
start, end = 0, 0
for i in xrange(len(data)):
if 'crypt' in data[i]:
start = i
break
data2 = data[::-1]
for j in xrange(len(data2)):
if 'crypt' in data2[j]:
end = j
break
for t in range(start, len(data)-end):
w.write(data[t])
使用AppDomain.UnhandledException或GUI / ASP.NET应用程序的类似catch-all事件可以观察到这些异常,但是使用这些事件进行常规异常处理是一种不可维护的方法(它会使应用程序崩溃)。
异步void方法具有不同的组合语义。返回任务或任务的异步方法可以使用await,Task.WhenAny,Task.WhenAll等轻松编写。返回void的异步方法不提供通知调用代码已完成的简单方法。启动几个异步void方法很容易,但要确定它们何时完成并不容易。 Async void方法将在启动和结束时通知其SynchronizationContext,但自定义SynchronizationContext是常规应用程序代码的复杂解决方案。
Async Void方法在使用同步事件处理程序时很有用,因为它们直接在SynchronizationContext上引发异常,这与同步事件处理程序的行为类似
有关详细信息,请查看此链接 https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
答案 3 :(得分:9)
调用异步void的问题在于,您甚至没有收回任务,也无法知道函数的任务何时完成(请参见https://blogs.msdn.microsoft.com/oldnewthing/20170720-00/?p=96655)
以下是调用异步函数的三种方法:
async Task<T> SomethingAsync() { ... return t; }
async Task SomethingAsync() { ... }
async void SomethingAsync() { ... }
在所有情况下,功能都会转换为一系列任务。区别在于函数返回什么。
在第一种情况下,该函数返回一个最终产生t的任务。
在第二种情况下,该函数返回一个没有积的任务,但是您可以 仍在等待它知道何时运行完成。
第三种情况令人讨厌。第三种情况类似于第二种情况,除了 您甚至都无法收回任务。你无法知道什么时候 该功能的任务已完成。
异步无效情况是“火灾和 “”:您启动了任务链,但是您不在乎它何时 完成。函数返回时,您所知道的就是 直到第一次等待执行。第一次等待后的一切 将来会在某个未指定的时间运行 访问。
答案 4 :(得分:5)
我认为你也可以使用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());
}
}
}
开始后台操作,只要你小心捕捉异常。想法?
{{1}}
答案 5 :(得分:0)
我的答案很简单 你可以等待空方法 错误CS4008无法等待'void'TestAsync e:\ test \ TestAsync \ TestAsync \ Program.cs
因此,如果该方法是异步的,则最好等待它,因为您可以失去异步优势。
答案 6 :(得分:-1)
According to Microsoft documentation,切勿使用async void
请勿执行以下操作:以下示例使用
async void
, 到达第一次等待时,HTTP请求完成:
在ASP.NET Core应用程序中,这总是不好的做法。
在HTTP请求完成后访问HttpResponse。
使进程崩溃。