112 votes

Travailler avec System.Threading.Timer en C #

J'ai un objet timer. Je veux qu'il soit exécuté à chaque minute. Plus précisément, il doit exécuter un OnCallBack méthode et devient inactif pendant un OnCallBack méthode est en cours d'exécution. Une fois un OnCallBack méthode de finitions, il a OnCallBack) redémarre une minuterie.

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, il semble ne pas fonctionner. Il court très vite toutes les 3 secondes. Même si élever une période (1000*10). Il me semble qu'il ferme les yeux sur 1000 * 10

Qu'ai-je fait de mal?

237voto

Ivan Zlatanov Points 2317

Ce n'est pas l'utilisation correcte du Système.Le filetage.Minuterie. Lorsque vous instanciez la Minuterie, vous devriez toujours faire ce qui suit:

_timer = new Timer( Callback, null, TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );

Ceci indiquera à la minuterie de cocher qu'une seule fois lorsque l'intervalle est écoulé. Ensuite, dans votre fonction de Rappel vous Changer 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 );
}

Ainsi, il n'est pas nécessaire pour les mécanismes de verrouillage, car il n'y a pas de simultanéité. La minuterie se déclenche le prochain rappel après le prochain intervalle + le temps de l'opération longue à exécuter.

Si vous avez besoin pour exécuter votre minuterie à exactement N millisecondes, alors je vous suggère de mesurer le temps de l'opération longue à exécuter à l'aide du Chronomètre, puis d'appeler la Modification de la méthode appropriée:

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

EDIT:

J'ai fortement encourager quelqu'un à faire .NET et est à l'aide de la CLR qui n'a pas lu Jeffrey Richter livre - CLR via C#, à lire dès que possible. Les minuteries et les pools de threads sont expliqué en détails.

14voto

Ivan Leonenko Points 508

Il n'est pas nécessaire d'arrêter le chronomètre, voir la solution intéressante de ce post :

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

9voto

Marco Mp Points 303

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).

3voto

Damien_The_Unbeliever Points 102139

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 d'ignorer la période de paramètre, puisque vous êtes en essayant de contrôler le periodicy vous-même.


Votre code d'origine est en cours d'exécution aussi rapide que possible, car vous garder en précisant 0 de la dueTime paramètre. D' Timer.Change:

Si dueTime est de zéro (0), la méthode de rappel est appelée immédiatement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X