2008-10-09 11 views
106

Juste ceci - Comment ajouter une minuterie à une application console C#? Ce serait bien si vous pouviez fournir un exemple de codage.Comment ajouter une minuterie à une application de console C#

+9

Attention: les réponses ici ont un bug, l'objet Timer va récupérer les ordures. La référence à la minuterie doit être stockée dans une variable statique pour s'assurer qu'elle continue à cocher. –

Répondre

91

C'est très agréable, mais afin de simuler un certain temps qui passe, nous devons exécuter une commande qui prend un certain temps et qui est très clair dans le deuxième exemple. Toutefois, le style d'utilisation d'une boucle for pour faire fonctionner certaines fonctionnalités à tout moment nécessite beaucoup de ressources de l'appareil et à la place, nous pouvons utiliser le récupérateur de place pour faire quelque chose comme ça.

Nous pouvons voir cette modification dans le code du même livre CLR Via C# Third Ed.

using System; 
using System.Threading; 

public static class Program { 

    public static void Main() { 
     // Create a Timer object that knows to call our TimerCallback 
     // method once every 2000 milliseconds. 
     Timer t = new Timer(TimerCallback, null, 0, 2000); 
     // Wait for the user to hit <Enter> 
     Console.ReadLine(); 
    } 

    private static void TimerCallback(Object o) { 
     // Display the date/time when this method got called. 
     Console.WriteLine("In TimerCallback: " + DateTime.Now); 
     // Force a garbage collection to occur for this demo. 
     GC.Collect(); 
    } 
} 
+3

Khalid, cela a été extrêmement utile. Merci. La console.readline() et le GC.Collect étaient exactement ce dont j'avais besoin. –

+0

J'ai fait commenter ma Console.ReadLine afin que le programme se termine après le premier rappel. Merci @SethSpearman – Dumisani

+5

@Ralph Willgoss, Pourquoi GC.Collect(); est requis? – Puchacz

60

Utilisez la classe System.Threading.Timer. System.Windows.Forms.Timer est principalement conçu pour être utilisé dans un thread unique, généralement le thread d'interface utilisateur Windows Forms.

Il existe également une classe System.Timers ajoutée au début du développement du framework .NET. Cependant, il est généralement recommandé d'utiliser la classe System.Threading.Timer à la place car il s'agit juste d'un wrapper autour de System.Threading.Timer.

Il est également recommandé de toujours utiliser un System.Threading.Timer statique (partagé dans VB.NET) si vous développez un service Windows et que vous avez besoin d'un temporisateur pour s'exécuter périodiquement. Cela évitera peut-être la récupération prématurée de votre objet timer.

Voici un exemple d'une minuterie dans une application console:

using System; 
using System.Threading; 
public static class Program 
{ 
    public static void Main() 
    { 
     Console.WriteLine("Main thread: starting a timer"); 
     Timer t = new Timer(ComputeBoundOp, 5, 0, 2000); 
     Console.WriteLine("Main thread: Doing other work here..."); 
     Thread.Sleep(10000); // Simulating other work (10 seconds) 
     t.Dispose(); // Cancel the timer now 
    } 
    // This method's signature must match the TimerCallback delegate 
    private static void ComputeBoundOp(Object state) 
    { 
     // This method is executed by a thread pool thread 
     Console.WriteLine("In ComputeBoundOp: state={0}", state); 
     Thread.Sleep(1000); // Simulates other work (1 second) 
     // When this method returns, the thread goes back 
     // to the pool and waits for another task 
    } 
} 

Extrait du livre CLR Via C# par Jeff Richter. En passant, ce livre décrit la logique derrière les 3 types de minuteurs dans le chapitre 23, hautement recommandé.

+0

Pouvez-vous fournir un peu plus d'informations sur le codage actuel? –

+0

L'exemple de msdn fonctionne pour vous? http://msdn.microsoft.com/fr-fr/library/system.threading.timer.aspx –

+0

Eric, je ne l'ai pas essayé mais ce ne serait pas inhabituel s'il y avait un problème avec ça. Je remarque qu'il essaie également de faire une sorte de synchronisation inter-thread, c'est toujours un domaine qui peut être difficile à corriger. Si vous pouvez l'éviter dans votre conception, il est toujours intelligent de le faire. – Ash

18

Voici le code pour créer une simple deuxième encoche à la minuterie:

using System; 
    using System.Threading; 

    class TimerExample 
    { 
     static public void Tick(Object stateInfo) 
     { 
      Console.WriteLine("Tick: {0}", DateTime.Now.ToString("h:mm:ss")); 
     } 

     static void Main() 
     { 
      TimerCallback callback = new TimerCallback(Tick); 

      Console.WriteLine("Creating timer: {0}\n", 
          DateTime.Now.ToString("h:mm:ss")); 

      // create a one second timer tick 
      Timer stateTimer = new Timer(callback, null, 0, 1000); 

      // loop here forever 
      for (; ;) 
      { 
       // add a sleep for 100 mSec to reduce CPU usage 
       Thread.Sleep(100); 
      } 
     } 
    } 

Et est ici la sortie résultante:

c:\temp>timer.exe 
    Creating timer: 5:22:40 

    Tick: 5:22:40 
    Tick: 5:22:41 
    Tick: 5:22:42 
    Tick: 5:22:43 
    Tick: 5:22:44 
    Tick: 5:22:45 
    Tick: 5:22:46 
    Tick: 5:22:47 

EDIT: Il est jamais une bonne idée d'ajouter Le disque dur tourne en code, car il consomme des cycles de processeur sans gain. Dans ce cas, cette boucle a été ajoutée juste pour empêcher la fermeture de l'application, ce qui permet d'observer les actions du thread. Mais par souci d'exactitude et pour réduire l'utilisation du processeur, un simple appel de veille a été ajouté à cette boucle.

+6

Le for (;;) {} provoque l'utilisation de 100% du processeur. –

+1

N'est-ce pas assez évident si vous avez une boucle infinie pour que cela se traduise par un CPU de 100%. Pour résoudre ce problème, tout ce que vous devez faire est d'ajouter un appel de veille à la boucle. – veight

+0

Est-ce que 'while (1)' est meilleur? Je n'ai jamais vu quelqu'un utiliser 'for (;;) {}' pour les boucles infinies mais toujours 'while (1)'. – Azimuth

4

Vous pouvez également utiliser vos propres mécanismes de synchronisation si vous voulez un peu plus de contrôle, mais peut-être moins de précision et plus de code/complexité, mais je recommanderais quand même une minuterie. Utilisez ce que si vous avez besoin d'avoir le contrôle sur le fil de synchronisation réelle:

private void ThreadLoop(object callback) 
{ 
    while(true) 
    { 
     ((Delegate) callback).DynamicInvoke(null); 
     Thread.Sleep(5000); 
    } 
} 

serait votre fil de synchronisation (modifier cela pour arrêter quand reqiuired, et quel que soit l'intervalle de temps que vous voulez).

et utiliser/démarrage que vous pouvez faire:

Thread t = new Thread(new ParameterizedThreadStart(ThreadLoop)); 

t.Start((Action)CallBack); 

Callback est votre méthode parameterless vide que vous voulez appelé à chaque intervalle. Par exemple:

private void CallBack() 
{ 
    //Do Something. 
} 
+1

Si je veux exécuter un travail par lots jusqu'à ce que le délai expire, votre suggestion serait-elle la meilleure? –

+0

S'il vous plaît dites-moi comment passer les paramètres à la méthode Thread .. Merci .. – olive

9

ou en utilisant Rx, court et doux:

static void Main() 
{ 
Observable.Interval(TimeSpan.FromSeconds(10)).Subscribe(t => Console.WriteLine("I am called... {0}", t)); 

for (; ;) { } 
} 
+1

la meilleure solution, vraiment! –

+4

très illisible et contre les meilleures pratiques. Il semble génial, mais ne devrait pas être utilisé dans la production, car certains ppl iront wtf et caca eux-mêmes. – ppumkin

+2

D'accord @ppumkin, cela viole de nombreux principes solides. – Alex

1

Vous pouvez également créer votre propre (si malheureux avec les options disponibles).

Créer votre propre implémentation Timer est assez basique.

Ceci est un exemple pour une application qui nécessitait un accès aux objets COM sur le même thread que le reste de mon code.

/// <summary> 
/// Internal timer for window.setTimeout() and window.setInterval(). 
/// This is to ensure that async calls always run on the same thread. 
/// </summary> 
public class Timer : IDisposable { 

    public void Tick() 
    { 
     if (Enabled && Environment.TickCount >= nextTick) 
     { 
      Callback.Invoke(this, null); 
      nextTick = Environment.TickCount + Interval; 
     } 
    } 

    private int nextTick = 0; 

    public void Start() 
    { 
     this.Enabled = true; 
     Interval = interval; 
    } 

    public void Stop() 
    { 
     this.Enabled = false; 
    } 

    public event EventHandler Callback; 

    public bool Enabled = false; 

    private int interval = 1000; 

    public int Interval 
    { 
     get { return interval; } 
     set { interval = value; nextTick = Environment.TickCount + interval; } 
    } 

    public void Dispose() 
    { 
     this.Callback = null; 
     this.Stop(); 
    } 

} 

Vous pouvez ajouter des événements comme suit:

Timer timer = new Timer(); 
timer.Callback += delegate 
{ 
    if (once) { timer.Enabled = false; } 
    Callback.execute(callbackId, args); 
}; 
timer.Enabled = true; 
timer.Interval = ms; 
timer.Start(); 
Window.timers.Add(Environment.TickCount, timer); 

Pour vous assurer la minuterie fonctionne besoin pour créer une boucle sans fin comme suit:

while (true) { 
    // Create a new list in case a new timer 
    // is added/removed during a callback. 
    foreach (Timer timer in new List<Timer>(timers.Values)) 
    { 
     timer.Tick(); 
    } 
} 
10

permet d'avoir un peu de plaisir

using System; 
using System.Timers; 

namespace TimerExample 
{ 
    class Program 
    { 
     static Timer timer = new Timer(1000); 
     static int i = 10; 

     static void Main(string[] args) 
     {    
      timer.Elapsed+=timer_Elapsed; 
      timer.Start(); 
      Console.Read(); 
     } 

     private static void timer_Elapsed(object sender, ElapsedEventArgs e) 
     { 
      i--; 

      Console.Clear(); 
      Console.WriteLine("================================================="); 
      Console.WriteLine("     DIFFUSE THE BOMB"); 
      Console.WriteLine(""); 
      Console.WriteLine("    Time Remaining: " + i.ToString()); 
      Console.WriteLine("");   
      Console.WriteLine("================================================="); 

      if (i == 0) 
      { 
       Console.Clear(); 
       Console.WriteLine(""); 
       Console.WriteLine("=============================================="); 
       Console.WriteLine("   B O O O O O M M M M M ! ! ! !"); 
       Console.WriteLine(""); 
       Console.WriteLine("    G A M E O V E R"); 
       Console.WriteLine("=============================================="); 

       timer.Close(); 
       timer.Dispose(); 
      } 

      GC.Collect(); 
     } 
    } 
} 
Questions connexes