2010-07-19 2 views
21

Je suis en train de lire un livre sur la bibliothèque parallèle de tâches C# et j'ai l'exemple suivant, mais le gestionnaire TaskScheduler.UnobservedTaskException n'est jamais déclenché. Quelqu'un peut-il me donner des indices quant à pourquoi?Le gestionnaire d'événements TaskScheduler.UnobservedTaskException n'est jamais déclenché

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

De quel livre s'agit-il? –

+0

Je serais vraiment curieux, aussi - l'exemple est incorrect, car il est impossible que l'événement soit soulevé dans cet exemple ... –

+1

Voici le livre: http://www.apress.com/book/view/1430229675 Programmation parallèle Pro .NET 4 en C# – devlife

Répondre

34

Malheureusement, cet exemple ne vous montrera jamais votre code. Le UnobservedTaskException ne se produira que si une tâche est collectée par le GC avec une exception non observée - tant que vous détenez une référence à task1 et task2, le GC ne collectera jamais, et vous ne verrez jamais votre gestionnaire d'exceptions.

Pour voir le comportement du UnobservedTaskException en action, je vais essayer suivant (exemple pièce):

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

Cela vous montrera vos messages. Le premier appel Thread.Sleep(100) fournit assez de temps pour les tâches à lancer. La collecte et l'attente forcent une collection GC, qui déclenchera votre gestionnaire d'événements 2x.

+0

Cela l'explique. Merci! – devlife

+6

Dans .NET 4.5, vous avez besoin de plus de vaudou pour que cela se produise. Tout d'abord, vous devez activer [ThrowUnobservedTaskException] (http://msdn.microsoft.com/en-GB/library/jj160346.aspx) dans app.config et, deuxièmement, vous devez générer le code dans la configuration Release (un point qui est documenté dans la section exemple de l'article MSDN lié). Je blâme cette mentalité «supprimons silencieusement les exceptions» sur JavaScript dans les navigateurs :) –

+2

@romkyns, je n'avais pas besoin de faire cela pour mon application .NET 4.5. –

3

L'exception ne sera pas "non observée" dans cet exemple d'extrait. Pas jusqu'à ce que le garbage collector se débarrasse des instances de tâche. Vous devrez le réécrire comme ceci:

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

Ne faites pas cela, utilisez Task.Wait().

0

Je lance ce code sur .NET 4.5, Visual Studio 2012 (Debug ou Release, n'a pas d'importance), je l'ai fait pas mis ThrowUnobservedTaskException dans mon app.config:

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

Et l'exception est intercepté par le gestionnaire UnobservedTaskException ("Caught!" est imprimé).

+0

Cela est dû au fait que 'ThrowUnobservedTaskException 'n'est nécessaire que si vous voulez revenir au comportement 4.0 d'une exception de tâche mettant fin au processus. Dans 4.5, la valeur par défaut est * non * et termine le processus. Il n'est pas nécessaire que l'exception non observée dans votre exemple soit interceptée par le gestionnaire d'événements. Plus d'informations dans les remarques [ici] (http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler.unobservedtaskexception% 28v = vs.110% 29.aspx) – BitMask777

Questions connexes