任务和exception沉默

为什么在任务中抛出的exception是静默exception,你永远不知道是否抛出了某个exception

try { Task task = new Task( () => { throw null; } ); task.Start(); } catch { Console.WriteLine("Exception"); } 

程序在完全沉默中成功运行! 线程的行为不同

 try { Thread thread = new Thread( () => { throw null; } ); thread .Start(); } catch { Console.WriteLine("Exception"); } 

在这种情况下,将抛出空指针exception。 有什么不同?

该场景的行为取决于您拥有的框架; 在4.0中,您实际上需要小心 – 如果您不处理TaskScheduler.UnobservedTaskException ,它将在以后收集/完成时出错,并将TaskScheduler.UnobservedTaskException 您的进程

 TaskScheduler.UnobservedTaskException += (sender, args) => { Trace.WriteLine(args.Exception.Message); // somebody forgot to check! args.SetObserved(); }; 

这改变了4.5,IIRC。

要检查可能失败的Task的结果,您可以注册一个延续 – 即调用ContinueWith并检查结果的exception。 或者,访问任务的.Result (也会执行隐式Wait() )将重新显示发生的exception。 观察任务的结果是很好的,因为它清除了终结标志,这意味着它可以更便宜地收集。

不,任务不是线程。 任务代表高级抽象 – 它们是本质上可并行化的工作单元。 线程运行工作单元。

在您的第一个示例中,您创建一个工作单元,然后告诉它自己运行(它是如何执行任务的实现细节)。 而在您的第二个示例中,您明确地安排了一个工作单元(它将以与Task的实现不同的方式出现)。

以下假定使用.NET 4.0,并使用默认的TaskScheduler。

首先请注意,在您传递的代理中引发了exception,因此它们是在不同的线程中引发的,而不是在您(逻辑上)正在执行catch线程中引发的。 因此,必须以某种方式将exception从执行委托/ lambda代码的线程传播到启动线程/任务的线程。

请注意,对于Task ,我认为库也可以选择不在它自己的线程上运行它,而是在调用线程上运行(但我不确定这是否仅适用于Parallel.ForEach等,而不是一个“裸”的Task对象)。

对于那两个发生,必须满足两件事:

  1. 调用线程仍处于活动状态。 否则,没有什么可以实际执行捕获。
  2. 线程/任务中引起的exception必须以某种方式保留并重新报告以便您捕获它。

话虽如此,你的两个例子都不会等待线程/任务完成。 在第一个例子中,你缺少一个task.Wait() (或类似的),在第二个例子中你缺少一个thread.Join() 。 根据您的测试代码计时行为,这可能意味着您可能永远无法从线程/任务(上面的第1项)中观察到exception。

即使你添加了两个调用,这对我来说也是如此(同样是.NET 4.0):

  • 任务示例:对task.Wait()的调用实际上会重新引发最初在任务委托中未处理的exception(这是TPL将在内部为您执行的操作),它确实将其包装在System.AggregateException ,您可以/将看看你是否使用了比平坦的“全能”更精确的东西。

  • 线程示例:委托引发的exception仍未处理,您的应用程序退出(除非您采取任何措施以不同方式处理未处理的exception )

换句话说,我会举例如下:

 // Thread example var thread = new Thread(() => { throw null; }); thread.Start(); thread.Join(); // Should never reach here, depending on timing sometimes not even // until the Join() call. The process terminates as soon as the other // thread runs the "throw null" code - which can logically happen somewhere // after the "start" of the "Start()" call. // Task example try { var task = new Task(() => { throw null; }); task.Start(); task.Wait(); } catch (AggregateException ex) { Console.WriteLine("Exception: " + ex); } 

我认为你没有在Task情况下获得exception,因为你没有在主线程中等待exception。 你只是继续。 把task.Wait()放在主线程中你会得到exception。