2014-09-05 117 views
7

我下面举个例子:(也请阅读代码中的注释,因为它会更有意义)异步任务抛出异常,如果没有等待?

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; 
} 
+0

您可能想看看TPL的[异常处理](http://msdn.microsoft.com/en-us/library/dd997415(v = vs.110).aspx)页面。 – 2014-09-05 17:56:52

回答

20

这是一个很大的问题打包成一个单一的“问题”,但行...

异步任务抛出异常,如果没有等待?

未观察到的任务异常由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更干净。

+0

嗨斯蒂芬,谢谢你的回答。你是对的这个例子是不正确的,该方法是一个非异步返回Task.Going试试你的建议谢谢 – 2014-09-05 21:37:25

+0

嘿@Dan Dinu,我知道这是一段时间,但你为什么不接受这样的好答案? – 2015-08-31 07:55:26

6

我对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); 
+0

这不回答所问的问题。另外,使用TPL的主要原因之一是它的错误处理功能非常强大。为什么你会用自己的方式回到错误处理的回调风格呢? – Servy 2014-09-05 20:05:30

+0

我可以看到它用于消防和忘记行动。例如,当我执行一组可以并行运行的任务时,我不想等待每个任务,但仍然希望抛出异常(即记录日志)。 – 2017-04-18 14:30:15

2

由于我不等待MyAsyncMethod中的实际结果,并且如果PostAsync方法引发异常,在哪种情况下异常将被抛出和处理?

如果你不await任何的任务,在你的代码或不分配的延续,行为可能取决于你使用.NET Framework的版本不同。在这两种情况下,返回Task会吞下例外,将出现差异,在最后确定:

  1. .NET 4.0 - 终结线程将重新抛出异常吞噬。如果没有注册全局异常处理程序,它将终止进程

  2. .NET 4.5及以上版本 - 异常将被吞噬并且不被注意。

在这两种情况下TaskScheduler.UnobservedTaskException事件将触发:

当故障任务的未观测到的异常即将触发异常升级策略,其中,默认情况下,将终止该进程时发生。

当非async任务返回方法执行同步异常立即将会传播,这就是为什么你捕获异常,而不使用await,但你一定要将取决于在你的代码。

有什么办法,我可以在我的组装

是的,你可以处理异常。我会建议你await你在你的程序集中执行的任务。

没有理由要使用async修改,如果你不等待任何东西:

public Task<Result> PostAsync() 
{ 
    return GetSomeTask(); 
} 

然后,您可以awaitPostAsync,赶上有例外:

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的方法。

+1

如果异步在'async'方法中抛出,即使在第一个'await'之前,它仍然会被包装在返回的Task中。它不会导致实际的方法调用抛出异常,即使它在同步运行时达到该异常。 – Servy 2014-09-05 20:38:33

+0

对,它是非异步'任务'返回方法。我修改了。 – 2014-09-05 20:47:12