2010-07-19 172 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

相關問題