it-swarm.com.de

Gibt es eine Möglichkeit, einen Thread auf unbestimmte Zeit anzuhalten?

Ich habe in meiner Freizeit an einer Web-Crawling-.NET-App gearbeitet, und eine der Funktionen dieser App, die ich einbeziehen wollte, war eine Pause-Schaltfläche zum Anhalten eines bestimmten Threads.

Ich bin relativ neu im Multithreading und habe noch keine Möglichkeit gefunden, einen derzeit unterstützten Thread auf unbestimmte Zeit anzuhalten. Ich kann mich nicht an die genaue Klasse/Methode erinnern, aber ich weiß, dass es eine Möglichkeit gibt, dies zu tun, aber sie wurde vom .NET-Framework als veraltet markiert.

Gibt es eine gute allgemeine Möglichkeit, einen Arbeitsthread in C # .NET auf unbestimmte Zeit anzuhalten?.

Ich hatte in letzter Zeit nicht viel Zeit, um an dieser App zu arbeiten, und das letzte Mal, als ich sie berührte, war sie im .NET 2.0-Framework. Ich bin offen für alle neuen Funktionen (falls vorhanden), die im .NET 3.5-Framework vorhanden sind, aber ich würde gerne eine Lösung kennen, die auch im 2.0-Framework funktioniert, da ich diese bei der Arbeit verwende und das wäre gut weiß nur für den Fall.

32
Dan Herbert

Verwenden Sie niemals Thread.Suspend. Das Hauptproblem dabei ist, dass Sie zu 99% nicht wissen können, was der Thread tut, wenn Sie ihn anhalten. Wenn dieser Thread eine Sperre enthält, können Sie leichter in eine Deadlock-Situation geraten usw. Beachten Sie, dass der von Ihnen aufgerufene Code möglicherweise Sperren im Hintergrund abruft/aufhebt. Win32 hat eine ähnliche API: SuspendThread und ResumeThread. Die folgenden Dokumente für SuspendThread geben eine schöne Zusammenfassung der Gefahren der API:

http://msdn.Microsoft.com/de-de/library/ms686345(VS.85).aspx

Diese Funktion ist hauptsächlich für die Verwendung durch Debugger vorgesehen. Es ist nicht für die Thread-Synchronisierung vorgesehen. Das Aufrufen von SuspendThread für einen Thread, der ein Synchronisationsobjekt besitzt, beispielsweise einen Mutex oder einen kritischen Abschnitt, kann zu einem Deadlock führen, wenn der aufrufende Thread versucht, ein Synchronisationsobjekt abzurufen, das einem angehaltenen Thread gehört. Um diese Situation zu vermeiden, sollte ein Thread in einer Anwendung, der kein Debugger ist, dem anderen Thread signalisieren, sich selbst zu suspendieren. Der Ziel-Thread muss so ausgelegt sein, dass er auf dieses Signal achtet und entsprechend reagiert.

Der richtige Weg, einen Thread auf unbestimmte Zeit anzuhalten, ist die Verwendung einer ManualResetEvent. Der Thread ist wahrscheinlich eine Schleife und führt einige Arbeit aus. Der einfachste Weg, den Thread zu unterbrechen, besteht darin, das Ereignis bei jeder Iteration wie folgt zu überprüfen:

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

    // Do some work...
}

Sie geben ein unbegrenztes Zeitlimit an. Wenn das Ereignis nicht signalisiert wird, wird der Thread auf unbestimmte Zeit blockiert, bis das Ereignis signalisiert wird, an welchem ​​Punkt der Thread an der Stelle fortfährt, an der er unterbrochen wurde.

Sie würden das Ereignis wie folgt erstellen:

ManualResetEvent _suspendEvent = new ManualResetEvent(true);

Der Parameter true weist das Ereignis an, im signalisierten Zustand zu starten.

Wenn Sie den Thread anhalten möchten, führen Sie folgende Schritte aus:

_suspendEvent.Reset();

Und um den Thread wieder aufzunehmen:

_suspendEvent.Set();

Sie können einen ähnlichen Mechanismus verwenden, um das Ende des Threads zu signalisieren und auf beide Ereignisse zu warten, um festzustellen, welches Ereignis gemeldet wurde.

Ich werde nur zum Spaß ein vollständiges Beispiel geben:

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 here..
        }
    }
}
91
Brannon

Das Threading in C # ebook fasst Thread.Suspend und Thread.Resume so zusammen:

Die veralteten Suspend- und Resume-Methoden haben zwei Modi - gefährlich und unbrauchbar!

Das Buch empfiehlt die Verwendung eines Synchronisationskonstrukts wie AutoResetEvent oder Monitor.Wait , um das Anhalten und Wiederaufnehmen von Threads durchzuführen.

15
Shabbyrobe

Ich habe gerade eine LoopingThread-Klasse implementiert, die eine an den Konstruktor übergebene Aktion wiederholt. Es basiert auf Brannons Post. Ich habe ein paar andere Dinge in diese Liste eingefügt, wie WaitForPause(), WaitForStop() und eine TimeBetween -Eigenschaft, die die Zeit angibt, die vor dem nächsten Looping gewartet werden soll.

Ich beschloss auch, die while-Schleife in eine do-while-Schleife zu ändern. Dies gibt uns ein deterministisches Verhalten für eine aufeinanderfolgende Start() und Pause(). Mit deterministisch meine ich, dass die Aktion mindestens einmal nach einem Start()-Befehl ausgeführt wird. In Brannons Implementierung ist dies möglicherweise nicht der Fall.

Ich habe einige Dinge für die Wurzel der Sache ausgelassen. Dinge wie "Überprüfen, ob der Thread bereits gestartet wurde" oder das IDisposable-Muster.

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));
  }
}
1
Matthias

In Übereinstimmung mit dem, was die anderen sagten - tu es nicht. Was Sie wirklich tun möchten, ist "Arbeit pausieren" und Ihre Threads frei herumlaufen zu lassen. Können Sie uns weitere Details zu den Threads geben, die Sie aussetzen möchten? Wenn Sie den Thread nicht gestartet haben, sollten Sie auf keinen Fall in Erwägung ziehen, ihn zu suspendieren - er gehört nicht Ihnen. Wenn es sich um Ihren Thread handelt, schlage ich vor, ihn nicht zu unterbrechen, sondern nur zu warten und auf weitere Arbeit zu warten. Brannon hat einige hervorragende Vorschläge für diese Option in seiner Antwort. Oder lassen Sie es einfach enden. und drehen Sie eine neue hoch, wenn Sie sie brauchen.

0
Bruce

Wenn es keine Synchronisationsanforderungen gibt:

Thread.Sleep(Timeout.Infinite);

0
Alberto

Neben den oben genannten Vorschlägen möchte ich noch einen Tipp hinzufügen. In einigen Fällen kann BackgroundWorker Ihren Code vereinfachen (insbesondere wenn Sie eine anonyme Methode verwenden, um DoWork und andere Ereignisse davon zu definieren).

0
Lex Li