Thread.Interrupt per fermare lungo sonno allo spegnimento app - Esiste un approccio migliore

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

  •  16-09-2019
  •  | 
  •  

Domanda

Sto avendo una piccola thread in background che si estende per tutta la durata delle applicazioni - tuttavia quando l'applicazione viene arrestato, il filo deve uscire con grazia

.

Il problema è che il filo viene eseguito un codice ad un intervallo di 15 minuti -. Che significa che può ospitare un SACCO

Ora, al fine di ottenere fuori di sonno, io lancio un interrupt a lui - la mia domanda è però, se c'è un approccio migliore a questo, in quanto gli interrupt generano ThreadInterruptedException

.

Ecco l'essenza del mio codice (un po '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)
        {
        }
    }
}
È stato utile?

Soluzione

Non c'è assolutamente un modo migliore - o utilizzare Monitor.Wait / Pulse invece di Sospensione / interruzione, o utilizzare un Auto / < a href = "http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx" rel = "nofollow noreferrer"> ManualResetEvent . (Si sarebbe probabilmente desidera una ManualResetEvent in questo caso).

Personalmente sono un fan di attesa / Pulse, probabilmente a causa di esso che è come attesa di Java () / notify () meccanismo. Tuttavia, ci sono sicuramente momenti in cui eventi di ripristino sono più utili.

Il tuo codice sarebbe simile a questa:

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

Per maggiori dettagli, si veda il mio filettatura esercitazione , in particolare le pagine su < a href = "http://www.yoda.arachsys.com/csharp/threads/deadlocks.shtml" rel = "nofollow noreferrer"> deadlock, in attesa e pulsante , la pagina su attesa gestisce . Joe Albahari ha anche un tutorial che copre gli stessi argomenti e li confronta.

Non ho guardato in dettaglio ancora, ma non sarei sorpreso se le estensioni parallele avevano anche alcune funzionalità per rendere questo più facile.

Altri suggerimenti

Si potrebbe utilizzare un evento per verificare se il processo deve finire così:

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

Non c'è classe CancellationTokenSource in .NET 4 e versioni successive che semplifica questo compito un po '.

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();
}

Non dimenticate che CancellationTokenSource è usa e getta, quindi assicuratevi di smaltire in modo corretto.

Un metodo potrebbe essere quello di aggiungere un evento di cancellare o delegare che il thread sottoscriverà. Quando l'evento cancel è invocare, il filo può fermarsi.

Sono assolutamente come Jon Skeets risposta. Tuttavia, questa potrebbe essere un po 'più facile da capire e dovrebbe anche funzionare:

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();
                }
            }
        }
    }
}

In alternativa, incluse le attese per il compito di sfondo in realtà essersi fermato (in questo caso, lo smaltimento deve essere richiamato da un altro thread diverso da quello del thread in background è in esecuzione, e, naturalmente, questo non è il codice perfetto, si presuppone che il lavoratore filo in realtà hanno iniziato):

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();
        }
    }
}

Se vedete eventuali difetti e gli svantaggi oltre soluzione Jon Skeets mi piacerebbe sentire loro come mi piace sempre imparare ;-) Credo che questo è più lento e utilizza più memoria e dovrebbe quindi non può essere utilizzato in larga scala e di breve lasso di tempo. Qualsiasi altra?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top