Thread.Interrupt para parar longo sono no desligamento app - Existe uma abordagem melhor

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

  •  16-09-2019
  •  | 
  •  

Pergunta

Eu estou tendo uma pequena discussão de fundo que se estende por toda a vida útil aplicações -. No entanto, quando a aplicação é desligamento, o segmento deve sair normalmente

O problema é que o segmento é executado algum código em um intervalo de 15 minutos -. O que significa que dorme um monte

Agora, para tirá-lo do sono, eu atirar uma interrupção para ele - a minha pergunta é, no entanto, se há uma melhor abordagem para isso, já que interrupções gerar ThreadInterruptedException

.

Aqui está a essência do meu código (um pouco 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)
        {
        }
    }
}
Foi útil?

Solução

Não há absolutamente uma maneira melhor - tanto para uso Monitor.Wait / pulso em vez de sono / interrupção, ou usar um Auto / < a href = "http://msdn.microsoft.com/en-us/library/system.threading.manualresetevent.aspx" rel = "nofollow noreferrer"> ManualResetEvent . (Você provavelmente quer um ManualResetEvent neste caso.)

Pessoalmente, eu sou um fã Wait / Pulse, provavelmente devido ao facto de ser como espera de Java () / notify () mecanismo. No entanto, há definitivamente momentos em que eventos de reposição são mais úteis.

Seu código seria algo parecido com isto:

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 mais detalhes, consulte o meu rosqueamento tutorial , em particular as páginas < a href = "http://www.yoda.arachsys.com/csharp/threads/deadlocks.shtml" rel = "nofollow noreferrer"> impasses, esperando e pulsando , a página em espera alças . Joe Albahari também tem um tutorial que cobre os mesmos tópicos e compara-los.

Eu não olhei em detalhes ainda, mas eu não ficaria surpreso se extensões paralelas também teve algumas funcionalidades para tornar isso mais fácil.

Outras dicas

Você pode usar um evento para verificar se o processo deve terminar assim:

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

Não é de classe CancellationTokenSource no .NET 4 e mais tarde o que simplifica esta tarefa um pouco.

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

Não se esqueça que CancellationTokenSource é descartável, por isso certifique-se de eliminá-lo corretamente.

Um método poderia ser a de adicionar um evento ou delegado que o fio vai se inscrever para cancelar. Quando a cancelar evento é invocar, a thread pode parar-se.

Eu absolutamente como Jon Skeets resposta. No entanto, este força ser um pouco mais fácil de entender e também deve 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();
                }
            }
        }
    }
}

Ou, incluindo esperando a tarefa de fundo para realmente ter parado (neste caso, descarte deve ser executado por outro segmento do que o segmento de segundo plano está em execução, e é claro que isso não é código perfeito, ele requer o trabalhador thread para realmente ter começado):

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 você ver todas as falhas e desvantagens sobre solução Jon Skeets eu gostaria de ouvi-los como eu sempre gosto de aprender ;-) Eu acho que isso é mais lenta e usa mais memória e não deve, portanto, ser usado em grande escala e curto espaço de tempo. Qualquer outra?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top