Compare usando Thread.Sleep e Timer para execução atrasada

Eu tenho um método que deve ser atrasado em execução 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 de Thread.Sleep é um projeto ruim. Mas eu não entendo o porque.

Mas, para usar o timer, o timer tem método de descarte. Como a execução está atrasada, não sei como descartar o Timer. Você tem alguma sugestão?

Ou se você tiver códigos alternativos para execução atrasada, também é importante.

Uma diferença é que System.Threading.Timer despacha o retorno de chamada em um thread de pool de threads, em vez de criar um novo thread toda vez. Se você precisar que isso aconteça mais de uma vez durante a vida útil do aplicativo, isso economizará a sobrecarga de criar e destruir vários segmentos (um processo que consome muitos resources, como indica o artigo que você mencionou), pois apenas reutilize threads no pool, e se você tiver mais de um timer ao mesmo tempo, significa que você terá menos threads rodando ao mesmo tempo (também economizando resources consideráveis).

Em outras palavras, o Timer será muito mais eficiente. Ele também pode ser mais preciso, já que Thread.Sleep só tem a garantia de esperar pelo menos o tempo que você especificar (o sistema operacional pode colocá-lo em suspensão por muito mais tempo). Concedido, Timer ainda não vai ser exatamente preciso, mas a intenção é triggersr o retorno de chamada o mais próximo do tempo especificado quanto possível, enquanto isso não é necessariamente a intenção de Thread.Sleep.

Quanto a destruir o Timer, o callback pode aceitar um parâmetro, então você pode ser capaz de passar o Timer em si como o parâmetro e chamar Dispose no callback (embora eu não tenha tentado isso – eu acho que é possível que o Timer pode ser bloqueado durante o retorno de chamada).

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

Talvez algo assim? (Mais uma vez, não tentei)

 class TimerState { public Timer Timer; } 

… e para iniciar o timer:

 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 do timer tente liberar o timer antes que o campo Timer tenha sido definido.


Adendo: Como o comentador apontou, se action () faz alguma coisa com a interface do usuário, então usar um System.Windows.Forms.Timer é provavelmente uma aposta melhor, já que ele executará o retorno de chamada no thread da UI. No entanto, se este não é o caso, e é para Thread.Sleep vs Threading.Timer, Threading.Timer é o caminho a percorrer.

use ThreadPool.RegisterWaitForSingleObject vez de timer:

 //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 que Thread.Sleep é bom se você realmente quiser pausar o aplicativo por um período de tempo especificado. Eu acho que a razão pela qual as pessoas dizem que é um projeto ruim é porque na maioria das situações as pessoas não querem que o aplicativo faça uma pausa.

Por exemplo, eu estava trabalhando em um cliente pop3 em que o programador estava usando Thread.Sleep (1000) para aguardar enquanto o soquete recuperava o email. Nessa situação, era melhor conectar um manipulador de events ao soquete e continuar a execução do programa após o soquete ter sido concluído.

Lembro-me de implementar uma solução semelhante à do Eric. Este é no entanto um 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 coisa que eu tenho com o System.Timer é que na maioria das vezes eu o vi usado para longos atrasos (horas, minutos) nos serviços de pesquisa e os desenvolvedores muitas vezes esquecem de iniciar o evento antes de iniciar o timer. Isso significa que, se eu iniciar o aplicativo ou serviço, tenho que esperar até que o cronômetro passe (horas, minutos) antes de ser realmente executado.

Claro, isso não é um problema com o cronômetro, mas acho que é muitas vezes usado incorretamente porque é muito fácil de usar mal.

@miniscalope Não, não use ThreadPool.RegisterWaitForSingleObject em vez do timer, System.Threading.Timer enfileirará um retorno de chamada a ser executado em um thread de pool de threads quando o tempo tiver passado e não requerer um handle de espera; amarrar um encadeamento de encadeamento de linhas esperando que o evento seja sinalizado ou que o tempo limite expire antes que o encadeamento chame o retorno de chamada.