【问题标题】:Why is this exception not caught?为什么这个异常没有被捕获?
【发布时间】:2013-05-14 17:05:03
【问题描述】:

我正在尝试运行以下代码:

class Program
{
    static void Main(string[] args)
    {
        var task = Task.Factory.StartNew(() =>
            {
                throw new ApplicationException("message");
            });
        try
        {
            task.ContinueWith(t => Console.WriteLine("End"));
        }
        catch (AggregateException aex)
        {
            Console.Write(aex.InnerException.Message);
        }
    }
}

我预计Exception 会在以下位置被捕获:

        catch (AggregateException aex)
        {
            Console.Write(aex.InnerException.Message);
        }

但这并没有发生。为什么会这样?

【问题讨论】:

  • 答案解决了做什么和如何做,而不是“为什么”(未捕获异常)。我认为理解这个基本原理很重要,Stephen Toub 的文章Task Exception Handling in .NET 4.5 对此进行了解释,这是必读]

标签: c# multithreading task-parallel-library


【解决方案1】:

您只是打印出task - 它甚至还没有完成。

打印出任务不会等待它完成,或者尝试获取值。

如果您将代码更改为:

try
{
    task.Wait();
}

...那么我希望它能够捕获异常。

(我之前使用的是Task<T>.Result,但我注意到这是一个没有返回值的任务,所以它只是非通用的Task。)

【讨论】:

  • @NominSim 否。当StartNew 返回时,委托本身还没有运行。当尚未达到异常时,它不能在该行上引发异常。
  • @NominSim:被阻塞的Result 属性获取捕获。
  • @NominSim ApplicationException 从另一个线程抛出。在那个调用提供的匿名委托的线程中,它最终会被捕获(在 Task 代码内部某处的 catch 块中)。创建任务时,它不会从主线程重新抛出。然后主线程等待任务,一旦任务完成,它会注意到任务以顶级异常结束;然后在主线程中重新抛出该异常(在包装的 AggregateException 中),因为这是 Wait 方法的实现明确所做的。
  • @NominSim 你在 VS 中调试吗?如果是这样,您可能会看到 VS 因第一次机会异常而中断(然后由 Task 捕获并存储。)在正常执行条件下,ApplicationException 永远不会被抛出 try/catch 块之外。跨度>
  • @NominSim:据我了解, t.ContinueWith 等待任务完成但不访问其结果;如果它确实会抛出异常。在任何情况下(成功或错误)都会执行此延续
【解决方案2】:

Task 的工作方式,最终调用您传递给StartNew 的委托的代码最终将被捕获,Exception 将存储在任务的实例字段中。可以通过查看 task.Exception 属性来检查该异常。 Console.WriteLine(task) 行只是在内部调用 task.ToString。该方法不会导致异常被抛出或重新抛出。

但是,在某些情况下,捕获的异常将被重新抛出。两个示例是访问Result 和调用Wait 时,以及当您await C# 5.0 中的一个任务时。

以下代码:

try
{
    task.Wait();
}
catch (AggregateException aex)
{
    Console.Write(aex.InnerException.Message);
}

将导致存储的异常被重新抛出并打印异常消息。

【讨论】:

  • 除了即使使用分号,您也不能单独使用属性访问作为语句。
  • @LuisFilipe 我已经编辑了他所指的错误。只需查看修订历史。
  • @LuisFilipe 实际上,我在 5 分钟内修复了它,所以它甚至不会出现在修订历史中;但我有一个错误,我已经修复了。
【解决方案3】:

您必须将 Visual Studio 配置为接受异常,即使这些异常不是您生成的 https://www.youtube.com/watch?v=UgJcISLX8BI

【讨论】:

    【解决方案4】:

    AggregateException 和 ApplicationException 都是同一个类 System.Exception 的子级。 AggregateException 不是一个 ApplicationException

    【讨论】:

    • 这无关紧要。 OP 的 try 块不会抛出任何类型的异常。
    • 是的,我要冒昧地说throw new ApplicationException("message"); 不是这个任务的真正实现。这可能是创建示例时的拼写错误,与实际问题无关。
    • @NickFreeman 不。这根本不是真的。 Task 类将从其主体中抛出的任何异常包装在 AggregateException 中,因此使用该异常是适当的。正如我在回答中所描述的,问题是 OP 需要使用task.Wait()。这一微小的变化使程序完全按预期工作。
    • @Servy 有趣。我不是不同意你,我是同意你的。我坚持我所说的一切,除了错字部分:P。我仍然认为 OP 的目标不是拥有一个功能强大的异常任务。我只是说抛出的异常与问题无关。
    【解决方案5】:

    因为你的语句不在 try 中,而是在它之前...catch 将捕获 try 大括号内的每个异常...

    【讨论】:

    • 我认为你误解了任务的本质。
    • 这根本不是真的。一个简单的反例:将try 块的主体设置为task.Result,它确实最终会重新抛出定义任务的委托中生成的异常。
    • @Jon Skeet > 看到反对票,我想我确实误解了整个问题:-)
    猜你喜欢
    • 2012-03-03
    • 2017-06-18
    • 1970-01-01
    • 2011-04-12
    • 2010-11-16
    • 2012-01-22
    相关资源
    最近更新 更多