Thread.Interrupt pour arrêter long sommeil à l'arrêt de l'application - Y at-il une meilleure approche

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

  •  16-09-2019
  •  | 
  •  

Question

Je vais avoir un petit fil d'arrière-plan qui fonctionne pour la durée de vie des applications - mais lorsque l'application est à l'arrêt, le thread doit quitter avec élégance

.

Le problème est que le fil passe du code à un intervalle de 15 minutes -. Ce qui signifie qu'il dort ALOT

Maintenant, pour le sortir du sommeil, je jette une interruption à ce - ma question est cependant, s'il y a une meilleure approche, puisque les interruptions génèrent ThreadInterruptedException

.

Voici l'essentiel de mon code (un peu 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)
        {
        }
    }
}
Était-ce utile?

La solution

Il n'y a absolument une meilleure façon - soit utiliser Monitor.Wait / Pulse au lieu de veille / Interrompre ou utilisez un Auto / < a href = "http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx" rel = "nofollow noreferrer"> ManualResetEvent . (Vous auriez probablement besoin d'un ManualResetEvent dans ce cas.)

Personnellement, je suis un WAIT / ventilateur Pulse, probablement en raison de son être comme mécanisme d'attente () / notify () Java. Cependant, il y a certainement des moments où les événements de réinitialisation sont plus utiles.

Votre code ressemblerait à quelque chose comme ceci:

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

Pour plus de détails, voir mon filetage tutoriel , en particulier les pages < a href = "http://www.yoda.arachsys.com/csharp/threads/deadlocks.shtml" rel = "nofollow noreferrer"> interblocages attente et pulsant , la page sur attente poignées. Joe Albahari dispose également d'un tutoriel qui couvre les mêmes sujets et les compare.

Je ne l'ai pas regardé en détail, mais je ne serais pas surpris si les extensions parallèles ont également certaines fonctionnalités pour vous faciliter la tâche.

Autres conseils

Vous pouvez utiliser un événement pour vérifier si le processus devrait se terminer comme ceci:

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

Il est classe CancellationTokenSource dans .NET 4 et versions ultérieures qui simplifie cette tâche un peu.

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

Ne pas oublier que CancellationTokenSource est disponible, assurez-vous de disposer correctement.

Une méthode peut-être ajouter un événement ou annuler déléguer que le fil souscrira. Lorsque l'événement est annuler invoquer, le fil peut s'arrêter.

Je suis absolument comme réponse Jon Skeets. Cependant, cette peut être un peu plus facile à comprendre et devrait également fonctionner:

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

Ou, y compris en attendant la tâche d'arrière-plan ont effectivement arrêté (dans ce cas, Éliminez doit être invoqué par un autre fil que celui du fil de fond est en cours d'exécution sur, et bien sûr ce n'est pas un code parfait, il exige que le travailleur fil d'avoir effectivement commencé):

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

Si vous voyez des défauts et des inconvénients sur une solution Jon Skeets je voudrais les entendre comme je l'apprécie toujours apprendre ;-) Je suppose que cela est plus lent et utilise plus de mémoire et ne doit donc pas être utilisé à grande échelle et à court délai. Toute autre?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top