2012-01-10 70 views
16

为什么在任务中抛出的异常沉默异常,你永远不知道,如果某异常被抛出任务和异常沉默

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"); 
} 

一个空指针异常会在这种情况下被抛出。 有什么区别?

回答

15

该场景的行为取决于您拥有的框架;在4.0中,您实际上需要小心 - 如果您未处理TaskScheduler.UnobservedTaskException,则在收集/最终确定后将错误后面的,并且会终止您的流程

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

这个变化在4.5,IIRC。

要检查Task威力失败,你可以注册一个延续的结果 - 即打电话ContinueWith和检查结果的异常。或者,访问该任务的.Result(这也将暗示Wait())将重新显示发生的异常。观察一项任务的结果是很好的,因为这样可以清除终结标志,这意味着它可以更便宜地收集。

+0

将版本号从5.0更改为4.5 - 希望您不介意。 (这将是C#5,但.NET 4.5。) – 2012-01-11 11:24:10

+0

@Jon根本没有;的确,感谢您纠正我。 – 2012-01-11 11:25:54

5

不,任务不是线程。任务代表了高层次的抽象 - 他们是一种本质上可并行的工作单元。线程运行工作单元。

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

+0

没问题,但是异常处理呢? – 2012-01-10 12:19:55

+0

OP更感兴趣的是为什么'Task'不会抛出,而'Thread'会抛出。 – Oded 2012-01-10 12:23:40

+2

@sleimanjneidi任务在询问任务时将存储例外情况。在一个线程中没有内在的异常处理,它会传播堆栈直到异常被捕获。 – 2012-01-10 12:24:07

1

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

+1

确实如此,但公平起见,在大多数情况下,我们不会立即调用Wait。 – 2012-01-10 12:38:13

4

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

所有公告首次将异常升高传递代表,所以他们都在不同的线程提出,而不是在你(逻辑)正在做你catch之一。所以不知何故,异常必须从执行委托/ lambda代码的线程传播到启动线程/任务的线程。

注意,对于Task,我认为库也可以选择不按它自己的线程中运行它,而是在调用线程(但我不知道这是唯一真正为Parallel.ForEach等,不适用于“裸”Task对象)。

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

  1. 调用线程仍然有效。否则没有什么可以实际执行捕获。
  2. 在线程/任务中引发的异常必须以某种方式保存并重新添加以供您捕获。

说了这么多,你的两个例子都不会等待线程/任务完成。在第一个例子中,你错过了task.Wait()(或类似的),第二个。根据您的测试代码计时行为,这可能意味着您可能永远无法观察到线程/任务的异常(上面的第1项)。

即使你加两个电话。这是我(再次.NET 4.0)会发生什么:

  • 任务例如:调用task.Wait()实际上reraises原本在任务的左侧未处理的异常委托人(这是TPL将在内部为您做的),它会将其包装在System.AggregateException内,您可以/将看到您是否会使用更精确的单位,然后是“全面通过”的单位。

  • 主题例如:通过代表提出的例外仍然未处理和你的应用程序退出(除非你做任何处理unhandled exceptions differently

换句话说,我将有示例如下:

// 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); 
}