2009-09-21 8 views
1

J'ai une classe statique qui crée plusieurs threads de travail dans son constructeur. Si une exception se produit avant que les travailleurs aient été créés, mon gestionnaire Application.ThreadException (utilisé pour fermer l'application si une erreur ne peut pas être récupérée) se déclenche normalement et tout va bien. Une fois que le premier thread de travail a été créé, en plus du déclenchement du handler, je reçois un message "MYAPP a rencontré un problème et doit être fermé." Nous sommes désolés de ce désagrément. " boîte de dialogue pour le rapport d'erreurs MS.Gestion des exceptions de constructeur statiques dans une application multithread

Dans cette instance spécifique, je peux réorganiser le code pour créer les derniers threads (après tout problème d'initialisation/d'accès aux ressources qui aurait pu déclencher une exception) mais ce n'est rien de plus qu'un problème et ne me donne pas toute information sur ce qui se passe réellement.

J'espère avoir excisé suffisamment de code de mon application pour montrer ce que j'essaie de faire ici.

class Program 
{ 
    /// <summary> 
    /// The main entry point for the application. 
    /// </summary> 
    [STAThread] 
    static void Main(string[] args) 
    { 
     try 
     { 
      Application.Run(theForm); 
      theForm.Dispose(); 

     } 
     catch (Exception e) 
     { 
      //doing this to use the same handler here and for Application.ThreadException 
      ThreadExceptionEventArgs argsEx = new ThreadExceptionEventArgs(e); 
      FatalExceptionHandler(null, argsEx); 

     } 
     finally 
     { 
      MyStaticClass.KillThreads(); 
     } 
    } 

    public static void FatalExceptionHandler(object sender, System.Threading.ThreadExceptionEventArgs ex) 
    { 
     Exception e = ex.Exception; 
     try 
     { 
      //lots of stuff to give more useful error messages for known problems 
      //and display them in a messagebox. 
     } 
     // if anything went wrong scraping the exception text for formatting, show the raw value. 
     catch 
     { 
      MessageBox.Show(e.Message); 
      return; 
     } 
     // after showing the MessageBox, close out the app. 
     finally 
     { 
      System.Environment.Exit(1); 
     } 
    } 
} 

class MyStaticClass 
{ 
    static MyStaticClass() 
    { 
     myThread = new Thread(new ThreadStart(SomeMethod)); 

     //if this exception is thrown everything works normally 
     //Throw new Exception("KABOOM"); 

     myThread.Start(); 

     //if this exception is thrown a windows error reporting dialog appears 
     //along with the messagebox from program.FatalExcetion handlder    
     //Throw new Exception("KABOOM"); 
    } 


    public void KillThreads() 
    { 
     //clean up the worker threads 
    } 
} 

Répondre

4

Les constructeurs statiques ne sont pas appelés à un moment précis au cours du démarrage de l'application. En particulier, si vous ne référencez jamais MyStaticClass à partir d'une autre classe, il est possible qu'il ne soit jamais initialisé. Puisque vous ne pouvez raisonnablement raisonner de la sorte, vous devez fournir une méthode StaticInitialize() (ou similaire) pour les types avec du code non trivial dans le constructeur statique plus les cas où le code d'initialisation statique doit vraiment s'exécuter. Déplacez le code du constructeur statique vers la méthode d'initialisation statique.

+0

Je pensais que le constructeur était toujours appelé, au plus tard, quand un membre/méthode/propriété de celui-ci a été accédé. –

+0

C'est correct, mais un "si et quand" le membre est accédé, et il n'aborde toujours pas a) le thread sur lequel le constructeur s'exécute et b) quand il s'exécute par rapport à la durée de vie de l'application. Voici une règle empirique que je viens de créer et qui pourrait vous aider: si le code dans le constructeur statique ne pouvait pas être exécuté deux fois de suite sans modifier le comportement du programme, déplacez le code vers une méthode d'initialisation statique. –

+0

Si je déplace tout le code du constructeur statique vers une méthode initialize, y a-t-il une raison de garder le constructeur autour? –

1

La valeur Application.ThreadException est uniquement augmentée pour les exceptions non gérées de threads d'interface utilisateur (puisqu'elle fait partie de la classe Application). L'événement d'exception de thread de traitement non géré est AppDomain.UnhandledException. Lorsqu'une exception non gérée sur un thread de travail se produit, cet événement est déclenché, puis la boîte de dialogue de panne du système s'affiche. Le AppDomain.UnhandledException est destiné uniquement à la consignation et il n'existe aucun moyen documenté d'empêcher l'affichage de la boîte de dialogue d'erreur de plantage système.

+0

Dans les deux cas, l'exception a été appelée dans la même méthode et doit provenir du même thread. Par conséquent, je ne comprends pas comment AppDomain.UnhandledException est impliqué. –

+0

De plus, y at-il un moyen de joindre un récupérateur d'exceptions blanket sur mes threads de travail pour empêcher quoi que ce soit d'aller à AppDomain.UnhandledException? –

+0

Je pense que le comportement de différence entre les deux endroits où vous lancez peut être attribué à la différence de temps. Dans un cas, le constructeur statique est exécuté sur le thread UI et est donc traité par le gestionnaire parapluie de l'application, dans un autre cas, en raison d'un timing différent, le constructeur est réellement exécuté par un thread de travail. . Malheureusement, il n'y a pas d'essai global pour les threads non-ui. –

Questions connexes