Eu tenho um objeto temporizador. Eu quero que seja executado a cada minuto. Especificamente, ele deve executar um OnCallBack
método e ficar inativo enquanto um OnCallBack
método está sendo executado. Quando um OnCallBack
método termina, ele (a OnCallBack
) reinicia um cronômetro.
Aqui está o que tenho agora:
private static Timer timer;
private static void Main()
{
timer = new Timer(_ => OnCallBack(), null, 0, 1000 * 10); //every 10 seconds
Console.ReadLine();
}
private static void OnCallBack()
{
timer.Change(Timeout.Infinite, Timeout.Infinite); //stops the timer
Thread.Sleep(3000); //doing some long operation
timer.Change(0, 1000 * 10); //restarts the timer
}
No entanto, parece não estar funcionando. Corre muito rápido a cada 3 segundos. Mesmo quando se levanta um período (1000 * 10). Parece que fecha os olhos para1000 * 10
O que eu fiz errado?
Timer.Change
: "Se dueTime for zero (0), o método de retorno de chamada é invocado imediatamente.". Parece que é zero para mim.Respostas:
Este não é o uso correto do System.Threading.Timer. Ao instanciar o Timer, você deve quase sempre fazer o seguinte:
Isso instruirá o cronômetro a marcar apenas uma vez quando o intervalo tiver decorrido. Em seguida, em sua função de retorno de chamada, você altera o cronômetro quando o trabalho é concluído, não antes. Exemplo:
Portanto, não há necessidade de mecanismos de bloqueio porque não há concorrência. O cronômetro irá disparar o próximo retorno de chamada após o próximo intervalo ter decorrido + o tempo da operação de longa duração.
Se você precisa executar seu cronômetro em exatamente N milissegundos, sugiro que você meça o tempo da operação de longa duração usando o cronômetro e chame o método Change apropriadamente:
Eu encorajo fortemente qualquer um que esteja usando .NET e esteja usando o CLR que não leu o livro de Jeffrey Richter - CLR via C # , para ler o mais rápido possível. Timers e pools de thread são explicados em detalhes aqui.
fonte
private void Callback( Object state ) { // Long running operation _timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite ); }
.Callback
pode ser chamado novamente antes que uma operação seja concluída.Long running operation
pode levar muito mais tempoTIME_INTERVAL_IN_MILLISECONDS
. O que aconteceria então?ThreadPool
, se você passar o cronômetro? Estou pensando em um cenário onde um novo thread é gerado para fazer um trabalho em um determinado intervalo - e então relegado para o pool de threads quando concluído.Não é necessário parar o cronômetro, veja uma boa solução neste post :
"Você poderia deixar o cronômetro continuar disparando o método de retorno de chamada, mas envolver seu código não reentrante em um Monitor.TenteEntrar / Sair. Não há necessidade de parar / reiniciar o cronômetro nesse caso; chamadas sobrepostas não obterão o bloqueio e retornarão imediatamente."
fonte
Está usando
System.Threading.Timer
obrigatório?Se não,
System.Timers.Timer
tem métodosStart()
e úteisStop()
(e umaAutoReset
propriedade que você pode definir como falsa, de forma que oStop()
não seja necessário e você simplesmente chamaStart()
após a execução).fonte
Eu apenas faria:
E ignore o parâmetro de período, já que você está tentando controlar o período por conta própria.
Seu código original está sendo executado o mais rápido possível, já que você continua especificando
0
odueTime
parâmetro. DeTimer.Change
:fonte
Change()
método?se você quiser, use algum fio de loop dentro ...
fonte