当返回值为void时,从async捕获异常等待
本文关键字:async 捕获异常 等待 返回值 void | 更新日期: 2023-09-27 18:30:00
我发现自己使用异步即发即弃方法,使用void作为返回值,但一定要注意异常。
人们似乎一致认为,如果没有对正在执行的Task的引用,那么async await就无法正确处理异常,而void应该是..嗯。。避免。。
我在下面的代码中缺少了什么,这些代码似乎可以完成任务:
class Program
{
static void Main()
{
var p = new Processor();
p.ExceptionThrown += p_ExceptionThrown;
for (var i = 0; i < 10; i++)
p.ProcessAsync(i);
Console.ReadKey();
}
static void p_ExceptionThrown(object sender, Exception e)
{
Console.WriteLine("Exception caught in Main : " + e);
}
}
class Processor
{
public async void ProcessAsync(int iteration)
{
try
{
await Task.Run(() => Process(iteration));
}
catch (Exception e)
{
ExceptionThrown?.Invoke(this, e);
}
}
static void Process(int iteration)
{
Thread.Sleep(500);
if(iteration == 5)
throw new Exception("AUUCH");
}
public event EventHandler<Exception> ExceptionThrown;
}
在async / await
关键字被编译到IL时,它们实际上会生成一个状态机,请在这里阅读。唯一应该使用async void
的时间是在事件处理程序上,如这里所解释的。问题是,当状态机构建出来时,它使用Task
或Task<T>
类作为返回类型,以便管理链中下一个异步操作的下一个状态。然而,当您将该方法定义为void时,它基本上将null
返回到状态机,然后一切都不正常。
catch 无法捕获异步void的异常
上面引用的是我之前向您介绍的最佳实践文章。下面的修改确实有效,因为我已经对它进行了测试以验证它确实有效。
class Program
{
static void Main()
{
var p = new Processor();
p.ExceptionThrown += p_ExceptionThrown;
for (var i = 0; i < 10; i++)
p.ProcessAsync(i);
Console.ReadKey();
}
static void p_ExceptionThrown(object sender, Exception e)
{
Console.WriteLine("Exception caught in Main : " + e);
}
}
class Processor
{
public async Task ProcessAsync(int iteration)
{
try
{
await Task.Run(() => Process(iteration));
}
catch (Exception e)
{
OnException(e);
}
}
public void Process(int iteration)
{
Thread.Sleep(500);
if(iteration == 5)
throw new Exception("AUUCH");
}
public event EventHandler<Exception> ExceptionThrown;
void OnException(Exception e)
{
var handler = ExceptionThrown;
if (handler != null)
handler(this, e);
}
}