Pergunta

Eu tenho um método que deve ser adiada funcionando por um período de tempo especificado.

Devo usar

Thread thread = new Thread(() => {
    Thread.Sleep(millisecond);
    action();
});
thread.IsBackground = true;
thread.Start();

ou

Timer timer = new Timer(o => action(), null, millisecond, -1);

Eu tinha lido alguns artigos sobre o uso Thread.Sleep é mau design. Mas eu realmente não entendo o porquê.

Mas para usar Timer, temporizador tem método dispose. Desde a execução está atrasado, eu não sei como eliminar temporizador. Você tem alguma sugestão?

Ou se você tiver códigos alternativos para execução retardada também estão apreciar.

Foi útil?

Solução

Uma diferença é que System.Threading.Timer despacha o retorno de chamada em um thread do pool, em vez de criar um novo segmento de cada vez. Se você precisa que isso aconteça mais de uma vez durante a vida de sua aplicação, isso vai salvar a sobrecarga de criar e destruir um monte de tópicos (um processo que é muito recurso intensivo, como o artigo que você referenciar pontos out), uma vez que irá apenas tópicos de reutilização na piscina, e se você vai ter mais do que um temporizador indo ao mesmo tempo significa que você terá menos threads em execução de uma só vez (também poupando recursos consideráveis).

Em outras palavras, Timer vai ser muito mais eficiente. Também pode ser mais preciso, já que Thread.Sleep só é garantido que esperar pelo menos contanto que a quantidade de tempo que você especificar (a OS pode colocá-lo para dormir por muito mais tempo). Concedido, Timer ainda não vai ser exatamente precisos, mas a intenção é acionar o retorno de chamada o mais próximo possível do tempo especificado quanto possível, o que não é necessariamente a intenção de Thread.Sleep.

Como para destruir o Timer, o retorno pode aceitar um parâmetro, então você pode ser capaz de passar o próprio Timer como parâmetro e chamar Dispose no retorno de chamada (embora eu não tentei isso - eu acho que é possível que o temporizador pode ser bloqueado durante o retorno de chamada).

Edit: Não, eu acho que você não pode fazer isso, pois você tem que especificar o parâmetro de retorno de chamada no próprio construtor Timer

.

Talvez algo como isso? (Mais uma vez, não têm realmente tentei)

class TimerState
{
    public Timer Timer;
}

... e para iniciar o temporizador:

TimerState state = new TimerState();

lock (state)
{
    state.Timer = new Timer((callbackState) => {
        action();
        lock (callbackState) { callbackState.Timer.Dispose(); }
        }, state, millisecond, -1);
}

O bloqueio deve impedir que o retorno de chamada timer de tentar libertar o timer antes do campo Timer ter sido definido.


Adendo: Como o comentarista apontou, se action() faz algo com a interface do usuário, em seguida, usando um System.Windows.Forms.Timer é provavelmente a melhor aposta, uma vez que irá executar o retorno de chamada no segmento. No entanto, se este não for o caso, e é até Thread.Sleep vs. Threading.Timer, Threading.Timer é o caminho a percorrer.

Outras dicas

ThreadPool.RegisterWaitForSingleObject em vez de temporizador:

//Wait 5 seconds then print out to console. 
//You can replace AutoResetEvent with a Semaphore or EventWaitHandle if you want to execute the command on those events and/or the timeout
System.Threading.ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(false), (state, bTimeout) => Console.WriteLine(state), "This is my state variable", TimeSpan.FromSeconds(5), true);

Eu acho Thread.Sleep é bom se você realmente quiser fazer uma pausa a pedido de um determinado período de tempo. Penso que a razão as pessoas dizem que é um projeto ruim é porque na maioria das situações as pessoas não realmente deseja que o aplicativo para fazer uma pausa.

Por exemplo, eu estava trabalhando em um cliente POP3 onde o programador estava usando Thread.Sleep (1000) para esperar enquanto o soquete recuperado mail. Nessa situação era melhor ligar um manipulador de eventos para o soquete e execução do programa continua após a tomada tinha terminado.

Lembro-me de implementar uma solução semelhante a um de Eric. Esta é no entanto um grupo de trabalho;)

class OneTimer
    {
        // Created by Roy Feintuch 2009
        // Basically we wrap a timer object in order to send itself as a context in order to dispose it after the cb invocation finished. This solves the problem of timer being GCed because going out of context
        public static void DoOneTime(ThreadStart cb, TimeSpan dueTime)
        {
            var td = new TimerDisposer();
            var timer = new Timer(myTdToKill =>
            {
                try
                {
                    cb();
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(string.Format("[DoOneTime] Error occured while invoking delegate. {0}", ex), "[OneTimer]");
                }
                finally
                {
                    ((TimerDisposer)myTdToKill).InternalTimer.Dispose();
                }
            },
                        td, dueTime, TimeSpan.FromMilliseconds(-1));

            td.InternalTimer = timer;
        }
    }

    class TimerDisposer
    {
        public Timer InternalTimer { get; set; }
    }

A única carne que eu tenho com o System.Timer se que na maioria das vezes eu já vi isso usado para longos atrasos (horas, minutos) em serviços de votação e os desenvolvedores muitas vezes se esqueça de lançar o evento Antes que iniciar o temporizador. Isto significa que se eu iniciar o aplicativo ou serviço, eu tenho que esperar até que os decorrido temporizador (horas, minutos) antes de realmente executa.

Claro, isso não é um problema com o timer, mas acho que seu frequentemente utilizados indevidamente por porque sua simplesmente muito fácil de uso indevido.

@miniscalope Não, não usar ThreadPool.RegisterWaitForSingleObject vez de timer, System.Threading.Timer vai fila uma chamada de retorno a ser executada em um thread do pool quando o tempo decorrido e não requer um identificador de espera, espera para único objeto vai amarrar uma espera fio threadpool para o evento a ser sinalizado ou o tempo limite para expirar antes que o segmento chama o retorno de chamada.

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