2010-07-19 146 views
21

我正在通读一本关于C#任务并行库的书,并且有以下示例,但TaskScheduler.UnobservedTaskException处理程序永远不会被触发。任何人都可以给我任何线索,为什么?TaskScheduler.UnobservedTaskException事件处理程序永远不会被触发

TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) => 
{ 
    eventArgs.SetObserved(); 
    ((AggregateException)eventArgs.Exception).Handle(ex => 
    { 
     Console.WriteLine("Exception type: {0}", ex.GetType()); 
     return true; 
    }); 
}; 

Task task1 = new Task(() => 
{ 
    throw new ArgumentNullException(); 
}); 

Task task2 = new Task(() => { 
    throw new ArgumentOutOfRangeException(); 
}); 

task1.Start(); 
task2.Start(); 

while (!task1.IsCompleted || !task2.IsCompleted) 
{ 
    Thread.Sleep(5000); 
} 

Console.WriteLine("done"); 
Console.ReadLine(); 
+3

这是什么书? – 2010-07-19 19:06:28

+0

我真的很好奇 - 这个例子是不正确的,因为在这个例子中不可能提出这个事件... – 2010-07-19 19:26:58

+1

这是这本书:http://www.apress.com/book/view/1430229675 C#中的.NET .NET 4并行编程# – devlife 2010-07-20 13:48:49

回答

34

不幸的是,该示例永远不会显示您的代码。 UnobservedTaskException只有在任务被GC收集时才会发生,并且未发现异常 - 只要您持有对task1task2的引用,GC将永远不会收集,并且您永远不会看到您的异常处理程序。

为了在行动中看到的UnobservedTaskException的行为,我想尝试下面的(人为的例子):

public static void Main() 
{ 
    TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) => 
       { 
        eventArgs.SetObserved(); 
        ((AggregateException)eventArgs.Exception).Handle(ex => 
        { 
         Console.WriteLine("Exception type: {0}", ex.GetType()); 
         return true; 
        }); 
       }; 

    Task.Factory.StartNew(() => 
    { 
     throw new ArgumentNullException(); 
    }); 

    Task.Factory.StartNew(() => 
    { 
     throw new ArgumentOutOfRangeException(); 
    }); 


    Thread.Sleep(100); 
    GC.Collect(); 
    GC.WaitForPendingFinalizers(); 

    Console.WriteLine("Done"); 
    Console.ReadKey(); 
} 

这将显示你的讯息。第一个Thread.Sleep(100)调用为任务提供了足够的时间。收集和等待会强制收集一个GC,这会激发您的事件处理程序2x。

+0

解释它。谢谢! – devlife 2010-07-20 13:50:17

+6

在.NET 4.5中,你需要额外的巫术才能发生这种情况。首先,您需要在app.config中启用[ThrowUnobservedTaskException](http://msdn.microsoft.com/en-GB/library/jj160346.aspx),其次,您需要在Release配置中构建代码(这一点仅在链接的MSDN文章的示例部分中有记录)。我在浏览器中将JavaScript怪罪于“让我们默默地禁止异常”的想法:) – 2013-04-30 14:05:14

+2

@romkyns,我不需要为我的.NET 4.5应用程序做这件事。 – 2015-06-17 10:07:41

3

该示例代码段中的异常不会被“未观察到”。直到垃圾收集器摆脱任务实例。你必须像这样重写它:

class Program { 
    static void Main(string[] args) { 

     TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) => 
     { 
      eventArgs.SetObserved(); 
      ((AggregateException)eventArgs.Exception).Handle(ex => 
      { 
       Console.WriteLine("Exception type: {0}", ex.GetType()); 
       return true; 
      }); 
     }; 

     Run(); 

     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 

     Console.WriteLine("done"); 
     Console.ReadLine(); 
    } 

    static void Run() { 
     Task task1 = new Task(() => { 
      throw new ArgumentNullException(); 
     }); 

     Task task2 = new Task(() => { 
      throw new ArgumentOutOfRangeException(); 
     }); 

     task1.Start(); 
     task2.Start(); 

     while (!task1.IsCompleted || !task2.IsCompleted) { 
      Thread.Sleep(50); 
     } 
    } 
} 

不要这样做,请使用Task.Wait()。

0

我运行.NET 4.5的代码时,Visual Studio 2012(调试或发布,无所谓),我做了在我的app.config不ThrowUnobservedTaskException

using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace Test 
{ 
    public static class Program 
    { 
     private static void Main() 
     { 
      TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; 

      RunTask(); 

      Thread.Sleep(2000); 

      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 
      GC.Collect(); 

      Console.ReadLine(); 
     } 

     static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) 
     { 
      Console.WriteLine("Caught!"); 
     } 

     private static void RunTask() 
     { 
      Task<int> task = Task.Factory.StartNew(() => 
      { 
       Thread.Sleep(1000); // emulate some calculation 
       Console.WriteLine("Before exception"); 
       throw new Exception(); 
       return 1; 
      }); 
     } 
    } 
} 

和异常被UnobservedTaskException处理程序捕获(“Caught!”被打印)。

+0

这是因为'ThrowUnobservedTaskException '只有在你想恢复到终止进程的任务异常的4.0行为时才是必需的。在4.5中,默认更改为* not *终止进程。您的示例中的未观察异常不需要被事件处理程序捕获。更多信息请参见[此处](http://msdn.microsoft.com/zh-cn/library/system。threading.tasks.taskscheduler.unobservedtaskexception%28V = vs.110%29.aspx) – BitMask777 2014-11-20 18:41:13

相关问题