Thread.Interrupt para detener largo sueño en el apagado aplicación - ¿Hay un mejor enfoque

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

  •  16-09-2019
  •  | 
  •  

Pregunta

Estoy teniendo un pequeño hilo de fondo que se extiende durante toda la vida aplicaciones - sin embargo, cuando la aplicación está apagado, el hilo debe salir con gracia

.

El problema es que el hilo ejecuta un código en un intervalo de 15 minutos -. Lo que significa que duerme ALOT

Ahora con el fin de sacarlo del sueño, lanzo una interrupción en ella - mi pregunta es, sin embargo, si hay una mejor aproximación a este, ya que las interrupciones generan ThreadInterruptedException

.

Esta es la esencia de mi código (algo 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)
        {
        }
    }
}
¿Fue útil?

Solución

No hay absolutamente una mejor manera - ya sea usar Monitor.Wait / pulso en lugar de Sleep / interrupción, o utilizar un Auto / < a href = "http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx" rel = "nofollow noreferrer"> ManualResetEvent . (Usted probablemente querrá un ManualResetEvent en este caso.)

En lo personal soy un fan de espera / Pulso, probablemente debido a que es como espera de Java () / notify () mecanismo. Sin embargo, definitivamente hay momentos en los casos de restauración son más útiles.

Su código sería algo como esto:

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

Para más detalles, véase mi roscado tutorial , en particular, las páginas de < a href = "http://www.yoda.arachsys.com/csharp/threads/deadlocks.shtml" rel = "nofollow noreferrer"> callejones sin salida, esperando y pulsante , la página en la espera maneja . Joe Albahari también tiene un tutorial que cubre los mismos temas y los compara.

No he mirado en detalle todavía, pero no me sorprendería si las extensiones paralelas también tenían algunas funciones para hacer esto más fácil.

Otros consejos

Se podría utilizar un evento para comprobar si el proceso debe terminar así:

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

No es la clase CancellationTokenSource en .NET 4 y más tarde lo que simplifica esta tarea un poco.

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

No se olvide que CancellationTokenSource es desechable, así que asegúrese de desechar correctamente.

Un método podría ser añadir un evento de cancelar o delegar que el hilo se va a suscribir. Cuando el evento es cancelar invocar, el hilo puede dejar de sí mismo.

Me absolutamente como respuesta Jon Skeets. Sin embargo, este podría ser un poco más fácil de entender y también debería funcionar:

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

O, incluyendo la espera de la tarea en segundo plano que en realidad se han parado (en este caso, disponga debe ser invocado por otro hilo que la que el hilo de fondo se está ejecutando en, y por supuesto esto no es código perfecto, se requiere que el trabajador hilo para haber comenzado en realidad):

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 hay defectos y desventajas con respecto a la solución Jon Skeets me gustaría oírlos como siempre me gusta aprender ;-) Supongo que esto es más lento y consume más memoria y por lo tanto no debe ser utilizado en una gran escala y el plazo corto. Cualquier otro?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top