2010-10-19 7 views
6

D'après mes recherches, je l'ai appris ce qui suit:TaskScheduler.UnobservedTaskException ne sera jamais appelé

  1. TaskScheduler.UnobservedTaskException doit attendre que la tâche à déchets collectés avant de cette exception inobservée tâche se propagera jusqu'au cas UnobservedTaskException.
  2. Si vous utilisez Task.Wait(), il ne sera jamais appelé car vous bloquez un résultat imminent de la tâche, par conséquent l'exception sera lancée sur Task.Wait() plutôt que sur l'événement UnobservedException.
  3. Appeler GC.Collect() manuellement est généralement une mauvaise idée, sauf si vous savez exactement ce que vous faites, d'où il est bon dans ce cas pour confirmer les choses, mais pas comme une bonne solution au problème.

Le problème

Si mes sorties d'application avant le garbage collector entre en jeu, je suis absolument 100% ne peut pas obtenir mon événement UnobservedTaskException au feu.

Notez le code suivant:

class Program 
{ 
    static void Main(string[] args) 
    { 
     TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; 
     Task.Factory.StartNew(() => 
     { 
      Console.WriteLine("Task started."); 
      throw new Exception("Test Exception"); 
     }); 
     Thread.Sleep(1000); 
     //GC.Collect(); 
     //GC.WaitForPendingFinalizers(); 
    } 

    static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) 
    { 
     File.WriteAllText(@"C:\data\TestException.txt", e.Exception.ToString()); 
     Console.WriteLine("EXCEPTION UNOBSERVED"); 
    } 
} 

Aucun fichier d'exception est écrit et rien est écrit à la console. 10-15 minutes et plus peuvent s'écouler après la sortie de l'application et je ne vois toujours aucune preuve que les restes de ma demande aient été récupérés. Vous pourriez demander, eh bien pourquoi ne pas simplement recueillir à la sortie? Eh bien, mon scénario dans le monde réel est que mon interception d'exceptions s'exécute dans un service WCF hébergé dans un service Windows. Je ne peux pas piéger lorsque le service Windows est en cours d'arrêt (et donc appeler manuellement GC.Collect()) car il n'y a pas d'événement pour autant que je puisse voir.

Où est-ce que je vais mal? Comment puis-je m'assurer que si quelque chose au fond du service WCF finit par briser mon service Windows, que j'ai une chance d'enregistrer l'exception avant que le service ne tombe?

Répondre

2

Nathan,

Vos points sont tous vrais. Effectuez les opérations suivantes:

namespace ConsoleApplication1 
{ 
    using System; 
    using System.Threading; 
    using System.Threading.Tasks; 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; 
      Task.Factory.StartNew(() => 
      { 
       Console.WriteLine("Task started."); 
       throw new Exception("Test Exception"); 
      }); 

      Thread.Sleep(1000); 
      Console.WriteLine("First Collect"); 
      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 
      Console.WriteLine("Waiting"); 
      Console.ReadKey(); 
     } 

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

J'ai remarqué que le débogueur « pièges » souvent l'événement de UnobservedTaskException, l'amenant à ne pas tirer correctement. Exécutez ce en dehors du débogueur, et il affichera "EXCEPTION UNOBSERVED" à chaque fois avant de s'éteindre.

+0

Salut, merci pour la suggestion, même si je l'exécute déjà en dehors du débogueur.Je remarque également que vous utilisez GC.Collect(). Que se passe-t-il dans votre exemple si vous ne collectez pas manuellement? –

+0

@Nathan: Normalement, il n'y a pas assez de pression GC pour que cela se produise dans cet exemple simple. Si le GC ne se rassemble pas, la "tâche" ne touchera pas le finaliseur et l'exception UnobservedException ne se déclenchera jamais. Cela n'arrive que lorsque le finaliseur recueille une tâche non-enracinée avec une exception. –

+0

@Nathan: Notez que UnobservedTaskException n'empêchera pas l'application de s'arrêter dans tous les cas - vous devez gérer vos exceptions dans votre tâche, ou toujours attendre une tâche, si vous voulez empêcher cela d'arrêter une application. –

5

Pour moi, TaskScheduler.UnobservedTaskException donne d'abord un très mauvais sentiment de sécurité. Cela ne vaut vraiment pas grand-chose si cela dépend de Garbage Collection.

J'ai trouvé la solution suivante, tirée de this msdn article, pour être beaucoup plus fiable. Il exécute essentiellement le bloc de continuation (où vous consignez l'exception) seulement s'il y avait des exceptions non gérées dans la tâche 1, et ne bloque pas l'exécution de l'interface utilisateur.

Vous pouvez également vouloir aplatir AggregateExceptions imbriquées et éventuellement créer une méthode d'extension, comme Reed Copsey depicted here.

var task1 = Task.Factory.StartNew(() => 
{ 
    throw new MyCustomException("Task1 faulted."); 
}) 
.ContinueWith((t) => 
{ 
    Console.WriteLine("I have observed a {0}", 
     t.Exception.InnerException.GetType().Name); 
}, 
TaskContinuationOptions.OnlyOnFaulted); 
Questions connexes