2010-12-22 3 views
0

Je suis en train de recréer les conditions qui causeront cette exception:Comment provoquer AggregateException avec TPL?

System.AggregateException: A Task's exception(s) were not observed 
either by Waiting on the Task or accessing its Exception property. 
As a result, the unobserved exception was rethrown by the finalizer thread.` 

J'ai écrit cette pensée de programme je fais l'exception, mais il ne fonctionne pas:

using System; 
using System.Threading.Tasks; 
namespace SomeAsyncStuff 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Task.Factory.StartNew(() => { throw new NullReferenceException("ex"); }); 
      GC.Collect(); 
      Console.WriteLine("completed");    
     } 
    } 
} 

Dans mon application réelle , J'utilise TPL et je n'ai pas codé ma gestion des exceptions correctement. En conséquence, j'ai cette exception. Maintenant j'essaye de recréer les mêmes conditions dans un programme séparé pour expérimenter des exceptions non observées.

Répondre

-1

Je suis l'OP. J'ai testé le GC.WaitForPendingFinalizers() mais cela n'a pas aidé à recréer l'exception. Le problème était que GC.Collect() était exécuté avant le démarrage de la tâche.

Ceci est le code correct pour recréer l'exception:

using System; 
using System.Threading; 
using System.Threading.Tasks; 
namespace SomeAsyncStuff 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Task.Factory.StartNew(() => { throw new NullReferenceException("ex"); }); 

      // give some time to the task to complete 
      Thread.Sleep(3000); 

      GC.Collect(); 
      // GC.WaitForPendingFinalizers(); 
      Console.WriteLine("completed"); 
     } 
    } 
} 
+0

Ceci n'est pas le code * correct * pour recréer l'exception. Appeler aléatoirement Thread.Sleep est une mauvaise pratique. Windows lui-même n'est pas un système d'exploitation en temps réel, donc l'utilisation de contraintes de temps sur les tâches est vouée à l'échec à un moment donné. Voir ma réponse pour une manière correcte de reproduire l'erreur. – scripni

+0

Comme j'ai écrit à John Wigger, utiliser task.Wait() ne reproduit pas ce que j'avais besoin de reproduire à l'époque (voir les commentaires dans sa réponse). Ma solution n'est pas quelque chose que l'on mettrait dans le code de production; J'essayais juste de recréer un bug intermittent que j'avais en production. Je pense que ma réponse est la meilleure façon de simuler ce qui s'est passé dans mon environnement de production. – Sylvain

4

Vous devrez peut-être ajouter un appel à GC.WaitForPendingFinalizers() après GC.Collect() puisque les finaliseurs s'exécutent sur leur propre thread.

3

L'exception est levée par le finaliseur TaskExceptionHolder, de sorte que le thread de finalisation doit être exécuté avant que cette exception ne soit levée. Comme Josh le fait remarquer, vous pouvez attendre que cela se produise en appelant le CG.WaitForPedingFinalizers().

Veuillez noter que ce comportement a été modifié dans le CTP Async actuel. J'ai parlé à Stephen Toub de l'équipe de PFX à ce sujet à TechEd Europe plus tôt cette année, et il a indiqué qu'ils ont dû le changer pour que la nouvelle fonctionnalité asynchrone fonctionne correctement. Donc même s'il est encore trop tôt pour dire quelque chose sur la prochaine version du framework, ce comportement pourrait très bien être changé dans la prochaine version.

1

@Sly, bien que vous êtes venu avec une réponse de travail, je pense que la plupart des gens seraient mieux servis en prêtant attention à la suggestion de message d'erreur » .. En attente de la tâche ... ". S'impliquer dans l'activité du GC est un signe que soit vous connaissez intimement le GC et vous avez un goulet d'étranglement au niveau des performances, soit vous manquez le point. Dans mon cas, cela signifie le dernier;) L'appel StartNew renvoie une tâche alors pourquoi ne pas l'utiliser? par exemple.

Tâche MyTask = Task.Factory.StartNew (() => { throw new NullReferenceException ("ex"); }); // donne du temps à la tâche pour terminer
myTask.Wait();

+0

J'essayais de recréer l'exception AggregateException afin d'expérimenter des exceptions non observées qui peuvent se produire lorsqu'un programme exécute des tâches de manière «fire and forget». Votre méthode simule un programme qui exécute des tâches dans un mode "feu et attendez les résultats". Je pense que l'appel GC.Collect() simule mieux un cas où un programme serait "surpris" par le GC entrant et éliminant le TaskExceptionHolder. – Sylvain

0

Je suis vraiment surpris que vous n'ayez pas essayé d'appeler correctement le code après la fin de la tâche, car qui peut dire que tout processus se terminera en moins de 3 secondes? Non seulement cela, mais cela lie d'autres processus pendant 3 secondes complètes. Je remplacerait cette implémentation par la méthode de tâche ContinueWith() pour appeler le GC après la fin de la tâche.

Task.Factory 
    .StartNew(() => { throw new NullReferenceException("ex"); }) 
    .ContinueWith(p => GC.Collect()); 

Si vous avez besoin bloc jusqu'à ce qu'il soit complet (pour votre exemple de code que vous utilisez pour debug), vous pouvez aussi faire un WaitOne après avoir commencé la tâche et ont ContinueWith() signaler le gestionnaire d'attente. Si vous devez le faire dans votre code de production, alors peut-être que ce que vous essayez d'accomplir est réellement synchrone, où vous n'avez pas besoin de vous soucier d'utiliser une tâche du tout.

-1

La manière la plus simple de recréer l'erreur est d'attendre la fin de la tâche.L'appel en attente bloque l'appel jusqu'à la fin de l'exécution de la tâche. L'appel en attente bloque l'appel.