当void为返回值时,从async-await捕获异常 [英] Catching exceptions from async-await when void is the return value
问题描述
我发现自己使用异步即发即弃方法,将void作为返回值,但一定要注意异常.
I find myself using async fire-and-forget methods using void as the return value, but DO care about exceptions.
It seems to be the consensus that exceptions cannot be handled properly with async-await if no reference is hold to the executing Task and void should be.. well.. avoided..
以下似乎可以完成此工作的代码缺少什么:
What am I missing in the following code that apparently seems to do the job:
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)
{
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);
}
}
推荐答案
async / await
关键字在被精简为IL时实际上会生成状态机,请阅读所述这里.问题在于,构建状态机时,它使用Task
或Task<T>
类作为返回类型,以便管理链中下一个异步操作的下一个状态.但是,将方法定义为void时,它基本上将null
返回到状态机,然后一切都摆脱了.
Under the covers the async / await
keywords actually generate a state-machine when they are compiled down to IL, please read about it here. The only time that you should ever use async void
is on an event handler, as explained here. The issue is that when the state machine is built-out it uses the Task
or Task<T>
classes as the return type in order to manage the next state of the next asynchronous operation in the chain. However, when you define the method as void, it basically returns null
to the state machine and then everything gets out of whack.
异步无效异常无法通过catch捕获
Exceptions from an async void can’t be caught with catch
以上引述摘自我之前指出的最佳实践文章.下面的更改确实有效,因为我已经对其进行了测试以验证它是否有效.
The quote from above is from the best practices article I pointed you to before. The below alteration does work, as I have tested it to verify that it does.
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);
}
}
这篇关于当void为返回值时,从async-await捕获异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!