Thread.Interrupt langen Schlaf in der App Abschaltung zu stoppen - Gibt es einen besseren Ansatz

StackOverflow https://stackoverflow.com/questions/1141617

  •  16-09-2019
  •  | 
  •  

Frage

Ich habe einen kleinen Hintergrund-Thread, die für die Anwendungen Lebensdauer läuft - aber wenn die Anwendung heruntergefahren wird, sollte der Faden ordnungsgemäß beenden

.

Das Problem ist, dass der Faden in einem Intervall von 15 Minuten einen Code läuft -. Das bedeutet, es schläft ALOT

Nun, um sie aus dem Schlaf zu bekommen, werfe ich einen Interrupt an ihn - meine Frage ist jedoch, ob es eine bessere Lösung für diesen, da Interrupts ThreadInterruptedException erzeugt

.

Hier ist der Kern meines Code (etwas pseudo):

public class BackgroundUpdater : IDisposable
{
    private Thread myThread;
    private const int intervalTime = 900000; // 15 minutes
    public void Dispose()
    {
        myThread.Interrupt();
    }

    public void Start()
    {
        myThread = new Thread(ThreadedWork);
        myThread.IsBackground = true; // To ensure against app waiting for thread to exit
        myThread.Priority = ThreadPriority.BelowNormal;
        myThread.Start();
    }

    private void ThreadedWork()
    {
        try
        {
            while (true)
            {
                Thread.Sleep(900000); // 15 minutes
                DoWork();
            }
        }
        catch (ThreadInterruptedException)
        {
        }
    }
}
War es hilfreich?

Lösung

Es gibt absolut eine bessere Art und Weise - entweder verwenden Monitor.Wait / Pulse statt Schlaf / Interrupt, oder verwenden Sie ein Auto / < a href = "http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx" rel = "nofollow noreferrer"> ManualResetEvent . (Sie würde wahrscheinlich eine ManualResetEvent in diesem Fall möchten.)

Persönlich bin ich ein Wait / Impuls-Fan, wahrscheinlich weil es wie Java warten zu sein () / notify () Mechanismus. Allerdings gibt es auf jeden Fall mal wo Reset Ereignisse sind nützlicher.

Ihr Code würde wie folgt aussehen:

private readonly object padlock = new object();
private volatile bool stopping = false;

public void Stop() // Could make this Dispose if you want
{
    stopping = true;
    lock (padlock)
    {
        Monitor.Pulse(padlock);
    }
}

private void ThreadedWork()
{
    while (!stopping)
    {
        DoWork();
        lock (padlock)
        {
            Monitor.Wait(padlock, TimeSpan.FromMinutes(15));
        }
    }
}

Weitere Informationen finden Sie in meinem Threading Tutorial , insbesondere die Seiten auf < a href = "http://www.yoda.arachsys.com/csharp/threads/deadlocks.shtml" rel = "nofollow noreferrer"> Deadlocks, warten und pulsierend , die Seite auf Wartegriffe . Joe Albahari hat auch ein Tutorial , die die gleichen Themen behandelt und vergleicht sie.

Ich habe im Detail noch nicht sieht, aber ich wäre nicht überrascht, wenn Parallel Extensions auch einige Funktionen, mußten dies einfacher zu machen.

Andere Tipps

Sie können ein Event nutzen, um Überprüfen Sie, ob der Prozess so enden sollte:

var eventX = new AutoResetEvent(false);
while (true)
{
    if(eventX.WaitOne(900000, false))
    {
        break;
    }
    DoWork();
}

Es gibt CancellationTokenSource Klasse in .NET 4 und höher, die diese Aufgabe ein wenig vereinfacht.

private readonly CancellationTokenSource cancellationTokenSource = 
    new CancellationTokenSource();

private void Run()
{
    while (!cancellationTokenSource.IsCancellationRequested)
    {
        DoWork();
        cancellationTokenSource.Token.WaitHandle.WaitOne(
            TimeSpan.FromMinutes(15));
    }
}

public void Stop()
{
    cancellationTokenSource.Cancel();
}

Vergessen Sie nicht, dass CancellationTokenSource ist Einweg, so stellen Sie sicher, dass Sie es richtig entsorgen.

Eine Methode könnte sein, ein Abbrechen Ereignis hinzuzufügen oder zu delegieren, dass der Faden abonnieren wird. Wenn das Abbrechen Ereignis aufrufen, kann der Thread selbst stoppen.

ich absolut wie Jon Skeets Antwort. Dies ist jedoch könnte ein bisschen leichter sein auch zu verstehen und soll funktionieren:

public class BackgroundTask : IDisposable
{
    private readonly CancellationTokenSource cancellationTokenSource;
    private bool stop;

    public BackgroundTask()
    {
        this.cancellationTokenSource = new CancellationTokenSource();
        this.stop = false;
    }

    public void Stop()
    {
        this.stop = true;
        this.cancellationTokenSource.Cancel();
    }

    public void Dispose()
    {
        this.cancellationTokenSource.Dispose();
    }

    private void ThreadedWork(object state)
    {
        using (var syncHandle = new ManualResetEventSlim())
        {
            while (!this.stop)
            {
                syncHandle.Wait(TimeSpan.FromMinutes(15), this.cancellationTokenSource.Token);
                if (!this.cancellationTokenSource.IsCancellationRequested)
                {
                    // DoWork();
                }
            }
        }
    }
}

Oder auch für die Hintergrundaufgabe wartet tatsächlich aufgehört haben zu (in diesem Fall muss Entsorgen von anderem Thread aufgerufen werden, als die, die der Hintergrund-Thread auf ausgeführt wird, und das ist natürlich nicht perfekt Code, bedarf es den Arbeiter Thread zu haben tatsächlich begonnen):

using System;
using System.Threading;

public class BackgroundTask : IDisposable
{
    private readonly ManualResetEventSlim threadedWorkEndSyncHandle;
    private readonly CancellationTokenSource cancellationTokenSource;
    private bool stop;

    public BackgroundTask()
    {
        this.threadedWorkEndSyncHandle = new ManualResetEventSlim();
        this.cancellationTokenSource = new CancellationTokenSource();
        this.stop = false;
    }

    public void Dispose()
    {
        this.stop = true;
        this.cancellationTokenSource.Cancel();
        this.threadedWorkEndSyncHandle.Wait();
        this.cancellationTokenSource.Dispose();
        this.threadedWorkEndSyncHandle.Dispose();
    }

    private void ThreadedWork(object state)
    {
        try
        {
            using (var syncHandle = new ManualResetEventSlim())
            {
                while (!this.stop)
                {
                    syncHandle.Wait(TimeSpan.FromMinutes(15), this.cancellationTokenSource.Token);
                    if (!this.cancellationTokenSource.IsCancellationRequested)
                    {
                        // DoWork();
                    }
                }
            }
        }
        finally
        {
            this.threadedWorkEndSyncHandle.Set();
        }
    }
}

Wenn Sie irgendwelche Mängel und Nachteile gegenüber Jon Skeets Lösung sehen möchte Ich mag, sie hören, wie ich immer genießen zu lernen ;-) Ich denke, dies ist langsamer und mehr Speicher verwendet und soll daher nicht in großem Umfang und kurze Zeit verwendet werden. Jede andere?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top