33 votes

Est-il possible pour une durée indéterminée interrompre un thread?

J'ai travaillé sur l'analyse web .NET application dans mon temps libre, et l'une des caractéristiques de cette appli que je voulais était un bouton pause pour arrêter un thread spécifique.

Je suis relativement nouveau pour le multi-threading et je n'ai pas été en mesure de trouver un moyen de mettre en pause un fil infini, qui est actuellement pris en charge. Je ne me souviens pas exactement de la classe/méthode, mais je sais qu'il existe un moyen de le faire, mais il a été marqué comme obsolète par la .NET framework.

Est-il un but d'intérêt général de façon indéfiniment interrompre un thread en C# .NET.

Je n'ai pas eu beaucoup de temps ces derniers temps à travailler sur cette application, et la dernière fois que j'ai touché c'était dans l' .NET framework 2.0. Je suis ouvert à toutes les nouvelles fonctionnalités (le cas échéant) qui existent dans le .NET framework 3.5, mais je voudrais savoir de solution qui fonctionne également dans les framework 2.0 puisque c'est ce que j'utilise au travail et il serait bon à savoir au cas où.

93voto

Brannon Points 12633

N'utilisez jamais d' Thread.Suspend. Le principal problème c'est que 99% du temps, vous ne pouvez pas savoir ce que le thread est en train de faire lorsque vous le suspendre. Si ce thread est verrouillé, vous rendre plus facile à obtenir dans une situation de blocage, etc. Gardez à l'esprit que le code que vous appelez peut-être l'acquisition/suppression des verrous derrière les coulisses. Win32 dispose d'une API similaire: SuspendThread et ResumeThread. Les documents suivants pour SuspendThread donne un bon résumé des dangers de l'API:

http://msdn.microsoft.com/en-us/library/ms686345(SV.85).aspx

Cette fonction est principalement conçu pour une utilisation par les débogueurs. Il n'est pas destiné à être utilisé pour la synchronisation des threads. L'appel de SuspendThread sur un thread qui possède un objet de synchronisation, comme un mutex ou de la section critique, peut conduire à un blocage si le thread appelant essaie d'obtenir un objet de synchronisation détenue par un thread suspendue. Pour éviter cette situation, un thread dans une application qui n'est pas un débogueur doit le signal de l'autre thread de suspendre lui-même. Le thread cible doit être conçu de manière à regarder pour ce signal et de réagir de façon appropriée.

La bonne façon de le suspendre à un fil indéfiniment est d'utiliser un ManualResetEvent. Le fil est le plus susceptible de bouclage, de l'exécution de certains travaux. Le moyen le plus facile de suspendre le fil est d'avoir la discussion "vérifier" l'événement chaque itération, comme suit:

while (true)
{
    _suspendEvent.WaitOne(Timeout.Infinite);

    // Do some work...
}

Vous spécifiez un délai d'attente infinie ainsi, lorsque l'événement n'est pas signalé, le thread va bloquer indéfiniment, jusqu'à ce que l'événement est signalé à quel point le thread va reprendre là où il l'avait laissé.

Vous devez créer l'événement comme suit:

ManualResetEvent _suspendEvent = new ManualResetEvent(true);

L' true paramètre indique à l'événement pour commencer dans l'état signalé.

Lorsque vous souhaitez interrompre le thread, vous effectuez les opérations suivantes:

_suspendEvent.Reset();

Et pour reprendre le fil:

_suspendEvent.Set();

Vous pouvez utiliser un mécanisme similaire pour signaler le fil de sortie et d'attente sur les deux événements, la détection de l'événement qui a été signalée.

Juste pour le plaisir, je vais vous donner un exemple complet:

public class Worker
{
    ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
    ManualResetEVent _pauseEvent = new ManualResetEvent(true);
    Thread _thread;

    public Worker() { }

    public void Start()
    {
        _thread = new Thread(DoWork);
        _thread.Start();
    }

    public void Pause()
    {
        _pauseEvent.Reset();
    }

    public void Resume()
    {
        _pauseEvent.Set();
    }

    public void Stop()
    {
        // Signal the shutdown event
        _shutdownEvent.Set();

        // Make sure to resume any paused threads
        _pauseEvent.Set();

        // Wait for the thread to exit
        _thread.Join();
    }

    public void DoWork()
    {
        while (true)
        {
            _pauseEvent.WaitOne(Timeout.Infinite);

            if (_shutdownEvent.WaitOne(0))
                break;

            // Do the work..
        }
    }
}

16voto

Shabbyrobe Points 4055

Le Filetage en C# ebook résume Fil.Suspendre et du Fil.Résumé ainsi:

Le obsolète Suspendre et Reprendre les méthodes ont deux modes inutile et dangereux!

Le livre recommande l'utilisation d'une synchronisation construire comme un AutoResetEvent ou Moniteur.Attendre pour effectuer fil de suspension et de reprise.

1voto

Matthias Points 2423

J'ai juste mis en place un LoopingThread classe qui boucle une action transmis au constructeur. Il est basé sur Brannon post. J'ai mis quelques autres trucs dans que, comme WaitForPause(), WaitForStop(), et un TimeBetween de la propriété, qui indique le temps qui devrait être attendu avant la prochaine boucle.

J'ai également décidé de changer le tout en boucle à faire en boucle. Cela nous donnera un comportement déterministe pour des périodes successives d'une Start() et Pause(). Avec déterministe je veux dire, que l'action est exécutée au moins une fois après un Start() commande. Dans Brannon de la mise en œuvre, cela pourrait ne pas être le cas.

J'ai omis certaines choses à la racine de la question. Des choses comme "vérifier si le thread a déjà commencé", ou l' IDisposable modèle.

public class LoopingThread
{
  private readonly Action _loopedAction;
  private readonly AutoResetEvent _pauseEvent;
  private readonly AutoResetEvent _resumeEvent;
  private readonly AutoResetEvent _stopEvent;
  private readonly AutoResetEvent _waitEvent;

  private readonly Thread _thread;

  public LoopingThread (Action loopedAction)
  {
    _loopedAction = loopedAction;
    _thread = new Thread (Loop);
    _pauseEvent = new AutoResetEvent (false);
    _resumeEvent = new AutoResetEvent (false);
    _stopEvent = new AutoResetEvent (false);
    _waitEvent = new AutoResetEvent (false);
  }

  public void Start ()
  {
    _thread.Start();
  }

  public void Pause (int timeout = 0)
  {
    _pauseEvent.Set();
    _waitEvent.WaitOne (timeout);
  }

  public void Resume ()
  {
    _resumeEvent.Set ();
  }

  public void Stop (int timeout = 0)
  {
    _stopEvent.Set();
    _resumeEvent.Set();
    _thread.Join (timeout);
  }

  public void WaitForPause ()
  {
    Pause (Timeout.Infinite);
  }

  public void WaitForStop ()
  {
    Stop (Timeout.Infinite);
  }

  public int PauseBetween { get; set; }

  private void Loop ()
  {
    do
    {
      _loopedAction ();

      if (_pauseEvent.WaitOne (PauseBetween))
      {
        _waitEvent.Set ();
        _resumeEvent.WaitOne (Timeout.Infinite);
      }
    } while (!_stopEvent.WaitOne (0));
  }
}

0voto

Lex Li Points 18214

À côté des suggestions ci-dessus, je voudrais ajouter un truc. Dans certains cas, l'utilisation BackgroundWorker peut simplifier votre code (en particulier lorsque vous utilisez la méthode anonyme à définir DoWork et d'autres événements de ce dernier).

0voto

Bruce Points 3473

En ligne avec ce que les autres ont dit: ne faites pas cela. Ce que vous voulez vraiment, c'est en "pause", et laissez votre fils errent librement. Pouvez-vous nous donner plus de détails sur le fil(s) que vous souhaitez suspendre? Si vous n'avez pas à démarrer le thread, vous ne devriez même pas envisager de suspendre - cela n'est pas le vôtre. Si c'est votre fil, puis-je suggérer au lieu de suspendre, il vous suffit de s'asseoir, en attendant plus de travail à faire. Brannon a quelques excellentes suggestions pour cette option dans sa réponse. Sinon, laissez-juste la fin; et faire tourner un nouveau lorsque vous en avez besoin.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X