异步任务抛出异常,如果没有等待?
我下面举个例子:(也请阅读代码中的注释,因为它会更有意义)异步任务抛出异常,如果没有等待?
public async Task<Task<Result>> MyAsyncMethod()
{
Task<Result> resultTask = await _mySender.PostAsync();
return resultTask;
// in real-life case this returns to a different assembly which I can't change
// but I need to do some exception handling on the Result in here
}
假设的PostAsync
方法_ mySender
看起来是这样的:
public Task<Task<Result>> PostAsync()
{
Task<Result> result = GetSomeTask();
return result;
}
的问题是:
因为我不在等待中的实际Result
,并且如果PostAsync
方法抛出异常,在哪个上下文是要抛出和处理的异常?
和
有什么办法可以处理我的程序集中的异常吗?
我很惊讶,当我试图改变MyAsyncMethod
到:
public async Task<Task<Result>> MyAsyncMethod()
{
try
{
Task<Result> resultTask = await _mySender.PostAsync();
return resultTask;
}
catch (MyCustomException ex)
{
}
}
异常被抓到这里来,事件是否有对应的实际结果没有任何的await。碰巧PostAsync
的结果已经可用,并且在这种情况下抛出异常吗?
是否可以使用ContinueWith
来处理当前类中的异常?例如:
public async Task<Task<Result>> MyAsyncMethod()
{
Task<Result> resultTask = await _mySender.PostAsync();
var exceptionHandlingTask = resultTask.ContinueWith(t => { handle(t.Exception)}, TaskContinuationOptions.OnlyOnFaulted);
return resultTask;
}
这是一个很大的问题打包成一个单一的“问题”,但行...
异步任务抛出异常,如果没有等待?
未观察到的任务异常由TaskScheduler.UnobservedTaskException
事件引发。这个事件是“最终”提出的,因为任务在其异常被认为未处理之前实际上必须被垃圾收集。
由于我不等待MyAsyncMethod中的实际结果,并且如果PostAsync方法引发异常,哪个上下文是要引发和处理的异常?
使用该async
修改,并返回一个Task
会把所有的异常对返回Task
任何方法。
有没有什么办法可以处理我的程序集中的异常?
是的,你可以更换返回任务,是这样的:
async Task<Result> HandleExceptionsAsync(Task<Result> original)
{
try
{
return await original;
}
catch ...
}
public async Task<Task<Result>> MyAsyncMethod()
{
Task<Result> resultTask = await _mySender.PostAsync();
return HandleExceptionsAsync(resultTask);
}
我很惊讶,当我试图改变MyAsyncMethod为[同步返回内部任务]异常被抓到这里来即使没有等待实际结果。
这实际上意味着您调用的方法不是async Task
,如代码示例所示。这是一种非返回方法,当这些方法之一抛出一个异常时,它就像其他任何异常一样被处理(即它直接通过调用堆栈;它不放在返回的Task
上)。
是否可以使用ContinueWith来处理当前类中的异常?
是的,但await
更干净。
嗨斯蒂芬,谢谢你的回答。你是对的这个例子是不正确的,该方法是一个非异步返回Task.Going试试你的建议谢谢 – 2014-09-05 21:37:25
嘿@Dan Dinu,我知道这是一段时间,但你为什么不接受这样的好答案? – 2015-08-31 07:55:26
我对Task
使用通用错误处理的扩展方法。这提供了一种记录所有错误的方法,并在发生错误时执行自定义操作。
public static async void ErrorHandle(this Task task, Action action = null)
{
try
{
await task.ConfigureAwait(false);
}
catch (Exception e)
{
Log.Error(e);
if (action != null)
action();
}
}
我倾向于使用它时,我做了“射后不理” Task
:
Task.Run(() => ProcessData(token), token).ErrorHandle(OnError);
这不回答所问的问题。另外,使用TPL的主要原因之一是它的错误处理功能非常强大。为什么你会用自己的方式回到错误处理的回调风格呢? – Servy 2014-09-05 20:05:30
我可以看到它用于消防和忘记行动。例如,当我执行一组可以并行运行的任务时,我不想等待每个任务,但仍然希望抛出异常(即记录日志)。 – 2017-04-18 14:30:15
由于我不等待MyAsyncMethod中的实际结果,并且如果PostAsync方法引发异常,在哪种情况下异常将被抛出和处理?
如果你不await
任何的任务,在你的代码或不分配的延续,行为可能取决于你使用.NET Framework的版本不同。在这两种情况下,返回Task
会吞下例外,将出现差异,在最后确定:
.NET 4.0 - 终结线程将重新抛出异常吞噬。如果没有注册全局异常处理程序,它将终止进程
.NET 4.5及以上版本 - 异常将被吞噬并且不被注意。
在这两种情况下TaskScheduler.UnobservedTaskException
事件将触发:
当故障任务的未观测到的异常即将触发异常升级策略,其中,默认情况下,将终止该进程时发生。
当非async
任务返回方法执行同步异常立即将会传播,这就是为什么你捕获异常,而不使用await
,但你一定要不将取决于在你的代码。
有什么办法,我可以在我的组装
是的,你可以处理异常。我会建议你await
你在你的程序集中执行的任务。
没有理由要使用async
修改,如果你不等待任何东西:
public Task<Result> PostAsync()
{
return GetSomeTask();
}
然后,您可以await
上PostAsync
,赶上有例外:
public async Task<Result> MyAsyncMethod()
{
try
{
// No need to use Task<Result> as await will unwrap the outter task
return await _mySender.PostAsync();
}
catch (MyCustomException e)
{
// handle here
}
}
你甚至可以进一步修改此代码并删除async
关键字,并可能捕获异常甚至更高的调用堆栈到调用MyAsyncMethod
的方法。
如果异步在'async'方法中抛出,即使在第一个'await'之前,它仍然会被包装在返回的Task中。它不会导致实际的方法调用抛出异常,即使它在同步运行时达到该异常。 – Servy 2014-09-05 20:38:33
对,它是非异步'任务'返回方法。我修改了。 – 2014-09-05 20:47:12
您可能想看看TPL的[异常处理](http://msdn.microsoft.com/en-us/library/dd997415(v = vs.110).aspx)页面。 – 2014-09-05 17:56:52