J'ai un objet timer. Je veux qu'il soit exécuté chaque minute. Plus précisément, il doit exécuter une méthode OnCallBack
et devient inactif pendant l'exécution d'une méthode OnCallBack
. Une fois qu'une méthode OnCallBack
est terminée, elle (une OnCallBack
) redémarre un minuteur.
Voici ce que j'ai en ce moment:
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
}
Cependant, cela ne semble pas fonctionner. Il court très vite toutes les 3 secondes. Même quand si augmenter une période (1000 * 10). Il semble que cela ferme les yeux sur 1000 * 10
Qu'ai-je fait de mal?
Ce n'est pas l'utilisation correcte de System.Threading.Timer. Lorsque vous instanciez le minuteur, vous devriez presque toujours procéder comme suit:
_timer = new Timer( Callback, null, TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );
Cela demandera à la minuterie de cocher une seule fois lorsque l'intervalle est écoulé. Ensuite, dans votre fonction de rappel, vous modifiez la minuterie une fois le travail terminé, pas avant. Exemple:
private void Callback( Object state )
{
// Long running operation
_timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );
}
Il n’ya donc pas besoin de mécanismes de verrouillage car il n’ya pas de concurrence. La minuterie déclenchera le prochain rappel une fois l'intervalle suivant écoulé + la durée de la longue opération.
Si vous devez exécuter votre minuterie à exactement N millisecondes, je vous suggère de mesurer le temps de la longue opération à l'aide de Chronomètre, puis appelez la méthode Change en conséquence:
private void Callback( Object state )
{
Stopwatch watch = new Stopwatch();
watch.Start();
// Long running operation
_timer.Change( Math.Max( 0, TIME_INTERVAL_IN_MILLISECONDS - watch.ElapsedMilliseconds ), Timeout.Infinite );
}
Je fortement encourage ceux qui font .NET et utilisent le CLR qui n'a pas lu le livre de Jeffrey Richter - CLR via C # , à lire dès que possible. Les timers et les pools de threads y sont expliqués en détail.
Il n'est pas nécessaire d'arrêter le chronomètre, voir la solution de Nice dans cet article :
"Vous pouvez laisser le minuteur continuer à déclencher la méthode de rappel mais enrouler votre code non réentrant dans un moniteur.
private void CreatorLoop(object state)
{
if (Monitor.TryEnter(lockObject))
{
try
{
// Work here
}
finally
{
Monitor.Exit(lockObject);
}
}
}
L'utilisation de System.Threading.Timer est-elle obligatoire?
Sinon, System.Timers.Timer a des méthodes pratiques Start () et Stop () (et une propriété AutoReset que vous pouvez définir sur false, de sorte que Stop () n'est pas nécessaire et que vous appelez simplement Start () après son exécution).
Je voudrais juste faire:
private static Timer timer;
private static void Main()
{
timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds
Console.ReadLine();
}
private static void OnCallBack()
{
timer.Dispose();
Thread.Sleep(3000); //doing some long operation
timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds
}
Et ignorez le paramètre de période, puisque vous essayez de contrôler vous-même la période.
Votre code d'origine fonctionne aussi rapidement que possible, car vous continuez à spécifier 0
pour le paramètre dueTime
. De Timer.Change
:
Si dueTime est à zéro (0), la méthode de rappel est immédiatement appelée.
var span = TimeSpan.FromMinutes(2);
var t = Task.Factory.StartNew(async delegate / () =>
{
this.SomeAsync();
await Task.Delay(span, source.Token);
}, source.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
source.Cancel(true/or not);
// or use ThreadPool(whit defaul options thread) like this
Task.Start(()=>{...}), source.Token)
si vous aimez utiliser un fil de boucle à l'intérieur ...
public async void RunForestRun(CancellationToken token)
{
var t = await Task.Factory.StartNew(async delegate
{
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(1), token)
.ContinueWith(task => { Console.WriteLine("End delay"); });
this.PrintConsole(1);
}
}, token) // drop thread options to default values;
}
// And somewhere there
source.Cancel();
//or
token.ThrowIfCancellationRequested(); // try/ catch block requred.