2009-08-04 6 views
2

J'ai une fonction que je veux autoriser à fonctionner pendant une période de temps donnée et ensuite, si elle ne l'a pas quittée, abandonner. Quelle est la meilleure façon de le faire?"Watch Dog" ou système de bac à sable en C#

Le meilleur que je l'ai pensé serait courir dans un autre thread, attendez avec un délai d'attente pour elle de mourir, puis utiliser Thread.Abort() pour le tuer (cela pourrait ne pas fonctionner si la fonction a le mauvais type de catch bloc). Une autre option (une que je ne sais pas comment faire du travail) serait une sorte de minuterie préemptive avec un lancer.

Y a-t-il un meilleur moyen? Une sorte de système de bac à sable facile?


Edit: la fonction que je vais être en cours d'exécution n'a pas de système pour vérifier si elle doit annuler et je ne peux pas (comme il ne faut pas ) ajouter. En outre, ceci est un harnais de test de sorte que la condition dans laquelle je vais tuer la fonction est qu'elle a coulé. Dans ce cas, je ne peux pas compter sur le fait de faire quoi que ce soit correctement.

Répondre

5

Cela peut être un peu exagéré pour quelque chose comme ceci, mais vous pouvez vérifier si vous voulez charger tout ce qui héberge ce thread dans un AppDomain. Un AppDomain est essentiellement un sandbox .NET.

Si le fil va dans les mauvaises herbes, vous pouvez simplement tuer l'AppDomain.

+0

Existe-t-il un moyen d'obtenir les valeurs de retour de fonction et le rappel pour fonctionner à travers une limite 'AppDomain'? – BCS

+0

Chargement du code dans un AppDomain est une bonne idée si vous ne pouvez pas utiliser un BackgroundWorker, mieux que Thread.Abort() –

+1

Il y a un très bon article avec du code wrapper pour appeler des méthodes arbitraires dans un autre AppDomain via un délégué: http: //blogs.geekdojo.net/richard/archive/2003/12/10/428.aspx. Cela a bien fonctionné pour moi pour récupérer les résultats de l'appel de méthode, mais pour une raison quelconque, je ne pouvais pas passer d'arguments au délégué sans qu'il se bloque. Peut-être que vous aurez plus de chance. –

1

Faites tout ce qui est en votre pouvoir pour trouver une solution saine à ce problème. Les exceptions ne sont pas le contrôle du flux, et l'abandon du thread est tellement pire. Cela dit, vous pouvez l'exécuter sur un fil et annuler le fil après un délai d'attente.

Edit: Voici un libellé fortement, mais valide, explication de combien je vous encourage à trouver une autre solution: http://tdanecker.blogspot.com/2007/08/do-never-ever-use-threadabort.html

+0

C'est tout un bon point. Cependant dans mon cas j'ai quelques considérations atténuantes: 1) La fonction peut être traitée comme une fonction pure parce qu'elle n'interagit pas avec l'état extérieur, 2) les effets secondaires de ne pas la tuer sont plus ou moins mortels et 3) I ne peut pas toucher le code qui doit être mis en boîte de sable donc j'ai vraiment besoin de faire un "arrêt maintenant peu importe quoi" – BCS

0

Vous pouvez utiliser Thread.Join() pour limiter l'exécution du fil.

2

Ce que vous voulez implémenter est un BackgroundWorker. Le BackgroundWorker peut exécuter une opération d'arrière-plan et fournit des mécanismes pour notifier cette opération d'annulation en arrière-plan. L'opération d'arrière-plan doit vérifier périodiquement la propriété Cancel sur l'instance DoWorkEventArgs transmise et se tuer elle-même si la valeur est true.

La définition de la propriété Cancel sur l'instance DoWorkEventArgs peut être effectuée en appelant la méthode CancelAsync sur l'instance BackgroundWorker d'origine.

Il existe un bon exemple dans le MSDN documentation. Voir aussi les contributions de la communauté et les liens connexes au bas de cette page.


Vous pouvez également trouver ce billet de blog intéressant, il utilise Thread.Abort bien que je recommande vraiment éviter ... C# Set method timeout using Generics

+0

Bonne solution générale +1 mais pas réalisable dans mon cas. – BCS

+0

Pourquoi? Pouvez-vous fournir des détails? –

+0

@ spoon16: Voir mes modifications et commentaires. Version courte: la même raison qui ne fonctionnerait pas si la fonction était dans une source fermée. – BCS

1

Pourquoi la fonction ne quitterait pas? Quel résultat produit-il? Mon premier essai serait de rendre le code assez serré et de gérer tous les cas "bloqués" de sorte que vous n'avez pas besoin d'un chien de garde - si vous attendez que les ressources soient libérées, mettez des délais d'attente sur les attentes, et gérer les exceptions de manière appropriée. En attendant, j'essayerais de générer un chien de garde hors processus, en procédant à Process.Kill si nécessaire. C'est plus efficace que Thread.Abort - si vous optez pour la solution brutale, pourquoi ne pas aller jusqu'au bout?

+0

le chien de garde ne devrait jamais tirer. Mais je ne devrais jamais avoir de bugs. La solution hors processus est un peu laide car l'obtention de données va être laide. – BCS

1

Je ne comprends peut-être pas le problème correctement, mais pourquoi ne pas mettre la logique dans la fonction elle-même? Exemple (C#):

public void someLongFunction() 
{ 
    DateTime start = DateTime.Now; 
    while (DateTime.Now <= start.AddMinutes(5)) // assuming 5 mins run time 
    { 
     // long processing code here 
     if (DateTime.Now > start.AddMinutes(5)) 
      break; 
     // more long processing code 
    } 
    // thread clean up etc. here 
} 
+0

En bref: je ne suis pas autorisé à. – BCS

0

Je sais que cela peut sembler exagéré, mais cela pourrait fonctionner techniquement la façon dont vous voulez. Diviser le code sur lequel vous travaillez en 2. La première partie serait un gestionnaire et la deuxième partie un exécuteur. L'exécuteur (dans votre cas) peut être une simple application de ligne de commande avec quelle que soit la fonction que vous voulez tester. À ce stade, votre application de gestion appelle l'application en ligne de commande à l'aide de la classe Process (System.Diagnostics). L'une des méthodes de la classe de processus est WaitForExit, qui attendra que votre console se ferme. Mais si l'application ne se termine pas, vous pouvez spécifier un délai d'attente avant de forcer l'application à quitter.

vous devriez à l'aide des être en mesure d'obtenir ce que vous voulez

+0

Obtenir args et retourner la valeur d'avant en arrière rend cela trop compliqué. Je serais presque plus disposé à vivre avec les cas d'erreur que cela. – BCS

1

Si vous ne pouvez pas interrompre facilement le processus de votre fonction, vous devrez peut-être utiliser une minuterie pour interrompre le thread en cours (il vous évite d'exécuter votre fonction dans un autre thread). Dès que le thread en cours est annulé, vous réactivez cet abandon avec Thread.ResetAbort() et vous pouvez exécuter les autres étapes de votre programme.

Vous pouvez donc utiliser une classe similaire à celui

using System.Threading; 
using System.Timers; 

namespace tools 
{ 
    public class ThreadAbortTimer 
    { 
     public ThreadAbortTimer(int timeout) 
     { 
      _CurrentThread = Thread.CurrentThread; 
      _Timer = new System.Timers.Timer(); 
      _Timer.Elapsed += _Timer_Elapsed; 
      _Timer.Interval = timeout; 
      _Timer.Enable = true; 
     } 

     /// <summary> 
     /// catch the timeout : if the current thread is still valid, it is aborted 
     /// </summary> 
     /// <param name="sender"></param> 
     /// <param name="e"></param> 
     void _Timer_Elapsed(object sender, ElapsedEventArgs e) 
     { 
      lock (typeof(ThreadAbortTimer)) 
      { 
       if (_CurrentThread != null) 
       { 
        _CurrentThread.Abort(); 
        _CurrentThread = null; 
       } 
      } 
     } 

     /// <summary> 
     /// timer that will check if the process lasts less than 30 seconds 
     /// </summary> 
     private readonly System.Timers.Timer _Timer; 

     /// <summary> 
     /// current thread to abort if the process is longer than 30 sec 
     /// </summary> 
     private Thread _CurrentThread; 

     /// <summary> 
     /// stop the timer 
     /// </summary> 
     public void Disable() 
     { 
      lock (typeof(ThreadAbortTimer)) 
      { 
       _Timer.Enabled = false; 
       _CurrentThread = null; 
      } 
     } 

     /// <summary> 
     /// dispose the timer 
     /// </summary> 
     public void Dispose() 
     { 
      _Timer.Dispose(); 
     } 
    } 
} 

et vous pouvez l'utiliser comme ceci:

using (var timer = new ThreadAbortTimer(timeout)) 
    { 
     try 
     { 
      // the process you want to timeout 
     } 
     catch 
     { 
      timer.Disable(); 
      Thread.ResetAbort(); 
     } 
    } 
+0

Un bon truc +1 mais ça ne devrait pas être un blocage? – BCS

+0

Vous ne devriez pas "verrouiller sur le type" - ce qui est intrinsèquement dangereux. Il suffit de google pour "ne pas verrouiller sur le type" pour plus d'informations. Utilisez un membre statique * private * (de type objet) à cette fin. Ou mieux encore, pour cet exemple, utilisez un membre d'instance privée - sinon toutes vos instances se synchroniseront entre elles. –

Questions connexes