c# - ContinueWith TaskContinuationOptions.OnlyOnFaulted 似乎没有捕获到从启动的任务中抛出的异常

标签 c# .net exception-handling task-parallel-library async-await

我正在尝试使用 ContinueWith 和 OnlyOnFaulted 捕获从任务方法抛出的异常,如下所示。但是,当我尝试运行此代码时出现未处理的异常。

我希望任务运行完成,因为我已经处理了异常。但是 Task.Wait() 遇到了 AggregateException。

var taskAction = new Action(() =>
{
    Thread.Sleep(1000); 
    Console.WriteLine("Task Waited for a sec");
    throw (new Exception("throwing for example"));
});
Task t = Task.Factory.StartNew(taskAction);
t.ContinueWith(x => Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+  t.Exception), TaskContinuationOptions.OnlyOnFaulted);
Console.WriteLine("Main thread waiting for 4 sec");
Thread.Sleep(4000);
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?");
Console.WriteLine("Task State: " + t.Status);
t.Wait();    

如果我像下面这样在任务方法中处理异常,一切都会像我预期的那样正常进行。任务运行到完成,异常被记录并且 Wait 也成功。

var taskAction = new Action(() =>
{
    try
    {
        Thread.Sleep(1000); 
        Console.WriteLine("Task Waited for a sec"); 
        throw (new Exception("throwing for example"));
    }
    catch (Exception ex)
    {
        Console.WriteLine("Catching the exception in the Action catch block only");
    }
});
Task t = Task.Factory.StartNew(taskAction);
t.ContinueWith(x=> Console.WriteLine("In the on Faulted continue with code. Catched exception from the task."+  t.Exception), TaskContinuationOptions.OnlyOnFaulted);
Console.WriteLine("Main thread waiting for 4 sec");
Thread.Sleep(4000);
Console.WriteLine("Wait of 4 secs complete..checking if task is completed?");
Console.WriteLine("Task State: " + t.Status);
t.Wait();    

我的问题是:我是在正确使用 OnlyOnFaulted 还是在任务方法本身中处理异常总是更好?即使任务遇到异常,我也希望主线程继续。另外,我想从任务方法中记录该异常。

注意:我必须等待任务方法完成才能继续(有或没有错误)。

总结(我目前的理解)

如果来自 Task 的异常被处理,即如果 wait 或 await 捕获异常,那么异常将传播到 continuedtask onfaulted。 即使在任务方法中也可以捕获异常并使用\处理。

try
{ 
   t.wait();
}
catch(Exception e)
{
   LogError(e);
}

在上面的例子中,在 LogError 被调用之前,与主任务的 onfaulted 关联的继续任务被执行。

最佳答案

首先,您没有正确使用 OnlyOnFaulted。当您在任务上使用 ContinueWith 时,您并没有真正更改该任务,您会返回一个任务继续(在您的情况下您会忽略它)。

如果原始任务出错(即其中抛出异常)它会保持出错状态(因此对其调用 Wait() 总是会重新抛出异常)。然而,延续将在任务出错后运行并处理异常。

这意味着在您的代码中您确实处理了异常,但您也使用 Wait() 重新抛出它。正确的代码应该是这样的:

Task originalTask = Task.Run(() => throw new Exception());
Task continuationTask = originalTask.ContinueWith(t => Console.WriteLine(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
continuationTask.Wait()
// Both tasks completed. No exception rethrown

现在,正如 Yuval Itzchakov 指出的那样,您可以在任何需要的地方处理异常,但如果可以的话,最好使用 async-await 异步等待(您不能在 Main) 而不是使用 Wait() 阻塞:

try
{
    await originalTask;
}
catch (Exception e)
{
    // handle exception
}

关于c# - ContinueWith TaskContinuationOptions.OnlyOnFaulted 似乎没有捕获到从启动的任务中抛出的异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27896613/

相关文章:

java - 处理从自定义转换器抛出的 Spring Boot REST 中的异常

java - 我们能否在发生异常的try block 中获取LineNumber和ColumnNumber

c++ - 异常规范如何影响虚拟析构函数覆盖?

c# - 如何让 XmlSerializer 忽略某种类型的所有成员?

c# - NUnit 嵌套集合比较

.net - 无法从 GAC 卸载程序集?

c# - Asp.net 5 beta8/rc1, 502指定的CGI应用程序,Azure [解决方案]

c# - 没有可用的连接来服务此操作 - Redis 与 StackExchange.Redis

c# - 单击按钮后如何更改模板

c# - 从 C# 源文件中提取文档注释