Attention: les réponses ici ont un bug, l'objet Timer va être ramassé. La référence à la minuterie doit être stockée dans une variable statique pour s'assurer qu'elle continue à cocher.
Hans Passant
@HansPassant Vous semblez avoir manqué la déclaration claire dans ma réponse: "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 minuteur pour s'exécuter périodiquement . Cela évitera éventuellement un garbage collection prématuré de votre objet timer. " Si les gens veulent copier un exemple aléatoire et l'utiliser à l'aveuglette, c'est leur problème.
Ash
Réponses:
120
C'est très bien, cependant, pour simuler le temps qui passe, nous devons exécuter une commande qui prend du temps et c'est très clair dans le deuxième exemple.
Cependant, le style d'utilisation d'une boucle for pour faire certaines fonctionnalités prend pour toujours beaucoup de ressources de l'appareil et à la place, nous pouvons utiliser le Garbage Collector pour faire quelque chose comme ça.
On peut voir cette modification dans le code du même livre CLR Via C # Third Ed.
using System;
using System.Threading;publicstaticclassProgram{publicstaticvoidMain(){// Create a Timer object that knows to call our TimerCallback// method once every 2000 milliseconds.Timer t =newTimer(TimerCallback,null,0,2000);// Wait for the user to hit <Enter>Console.ReadLine();}privatestaticvoidTimerCallback(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();}}
Khalid, cela a été extrêmement utile. Merci. La console.readline () et le GC.Collect étaient exactement ce dont j'avais besoin.
Seth Spearman
9
@Ralph Willgoss, Pourquoi GC.Collect (); est requis?
Puchacz
2
@Puchacz Je ne vois pas l'intérêt d'appeler GC.Collect(). Il n'y a rien à collecter. Cela aurait du sens, si GC.KeepAlive(t)était appelé aprèsConsole.ReadLine();
newprint
1
Il s'est terminé après le premier rappel
BadPiggie
1
@Khalid Al Hajami "Cependant, le style d'utilisation d'une boucle for pour faire certaines fonctionnalités prend à jamais beaucoup de ressources de l'appareil et à la place nous pouvons utiliser le Garbage Collector pour faire quelque chose comme ça." Ce sont des ordures absolument absurdes. Le ramasse-miettes n'est absolument pas pertinent. Avez-vous copié ceci à partir d'un livre sans comprendre ce que vous copiez?
Ash
66
Utilisez la classe System.Threading.Timer.
System.Windows.Forms.Timer est principalement conçu pour être utilisé dans un seul thread, 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 ne s'agit que d'un wrapper autour de System.Threading.Timer de toute façon.
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'une minuterie pour s'exécuter périodiquement. Cela évitera éventuellement un garbage collection prématuré de votre objet timer.
Voici un exemple de minuterie dans une application console:
using System;
using System.Threading;publicstaticclassProgram{publicstaticvoidMain(){Console.WriteLine("Main thread: starting a timer");Timer t =newTimer(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 delegateprivatestaticvoidComputeBoundOp(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 # de Jeff Richter. En passant, ce livre décrit la logique derrière les 3 types de minuteries dans le chapitre 23, fortement recommandé.
Eric, je ne l'ai pas essayé mais ce ne serait pas inhabituel s'il y avait un problème avec. Je remarque qu'il essaie également de faire une sorte de synchronisation inter-thread, c'est toujours un domaine qui peut être difficile à faire. Si vous pouvez l'éviter dans votre conception, il est toujours judicieux de le faire.
Ash
1
Ash - Je suis tout à fait d'accord avec les exemples msdn. Cependant, je n'écarterais pas immédiatement le code de synchronisation, si le minuteur s'exécute dans son propre thread, alors vous écrivez une application multithread et vous devez être conscient des problèmes liés à la synchronisation.
Eric Tuttleman
1
Que se passe-t-il s'il existe plusieurs méthodes qui correspondent à la signature du délégué TimerCallback?
Ozkan
22
Voici le code pour créer un simple tick de minuterie d'une seconde:
using System;
using System.Threading;classTimerExample{staticpublicvoidTick(Object stateInfo){Console.WriteLine("Tick: {0}",DateTime.Now.ToString("h:mm:ss"));}staticvoidMain(){TimerCallback callback =newTimerCallback(Tick);Console.WriteLine("Creating timer: {0}\n",DateTime.Now.ToString("h:mm:ss"));// create a one second timer tickTimer stateTimer =newTimer(callback,null,0,1000);// loop here foreverfor(;;){// add a sleep for 100 mSec to reduce CPU usageThread.Sleep(100);}}}
EDIT: Ce n'est jamais une bonne idée d'ajouter des boucles hard spin dans le code car elles consomment des cycles CPU sans gain. Dans ce cas, cette boucle a été ajoutée juste pour empêcher l'application de se fermer, 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 Sleep a été ajouté à cette boucle.
Le for (;;) {} provoque une utilisation à 100% du processeur.
Seth Spearman
1
N'est-ce pas assez évident si vous avez une boucle for infinie, cela se traduira 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
3
Il est étonnant de voir combien de personnes se demandent si la boucle for doit être une boucle while et pourquoi le processeur passe à 100%. Parlez de manquer le bois pour les arbres! Azimuth, je voudrais personnellement savoir comment un while (1) serait différent de l'infini pour la boucle? Les personnes qui écrivent l'optimiseur du compilateur CLR s'assureront-elles que ces deux constructions de code créent exactement le même code CLR?
Blake7
1
Une des raisons pour lesquelles while (1) ne fonctionnera pas est qu'il n'est pas valide c #: test.cs (21,20): erreur CS0031: La valeur constante «1» ne peut pas être convertie en «
booléen
1
Pas sur ma machine (win8.1, i5), seulement environ 20-30%, quel type d'ordinateur aviez-vous à l'époque? @SethSpearman
shinzou
17
Permet de s'amuser un peu
using System;
using System.Timers;
namespace TimerExample{classProgram{staticTimer timer =newTimer(1000);staticint i =10;staticvoidMain(string[] args){
timer.Elapsed+=timer_Elapsed;
timer.Start();Console.Read();}privatestaticvoid timer_Elapsed(object sender,ElapsedEventArgs e){
i--;Console.Clear();Console.WriteLine("=================================================");Console.WriteLine(" DEFUSE 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();}}}
très illisible et contraire aux meilleures pratiques. Cela a l'air génial mais ne devrait pas être utilisé en production car certains ppl iront wtf et caca eux-mêmes.
Piotr Kula
2
Les extensions réactives (Rx) n'ont pas été activement développées depuis 2 ans. De plus, les exemples sont sans contexte et déroutants. Peu de diagrammes ou d'exemples de flux à connaître.
James Bailey
4
Vous pouvez également utiliser vos propres mécanismes de chronométrage 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 un minuteur. Utilisez ceci si vous avez besoin de contrôler le thread de synchronisation réel:
Si je veux exécuter un travail par lots jusqu'à ce qu'il expire, votre suggestion serait-elle la meilleure?
Johan Bresler
1
Vous pouvez également créer le vôtre (si vous n'êtes pas satisfait des options disponibles).
Créer votre propre Timerimplémentation est assez basique.
Ceci est un exemple pour une application qui avait besoin d'un accès aux objets COM sur le même thread que le reste de ma base de code.
/// <summary>/// Internal timer for window.setTimeout() and window.setInterval()./// This is to ensure that async calls always run on the same thread./// </summary>publicclassTimer:IDisposable{publicvoidTick(){if(Enabled&&Environment.TickCount>= nextTick){Callback.Invoke(this,null);
nextTick =Environment.TickCount+Interval;}}privateint nextTick =0;publicvoidStart(){this.Enabled=true;Interval= interval;}publicvoidStop(){this.Enabled=false;}publiceventEventHandlerCallback;publicboolEnabled=false;privateint interval =1000;publicintInterval{get{return interval;}set{ interval =value; nextTick =Environment.TickCount+ interval;}}publicvoidDispose(){this.Callback=null;this.Stop();}}
Pour vous assurer que la minuterie fonctionne, vous devez 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 innewList<Timer>(timers.Values)){
timer.Tick();}}
publicstaticvoidMain(){SetTimer();Console.WriteLine("\nPress the Enter key to exit the application...\n");Console.WriteLine("The application started at {0:HH:mm:ss.fff}",DateTime.Now);Console.ReadLine();
aTimer.Stop();
aTimer.Dispose();Console.WriteLine("Terminating the application...");}privatestaticvoidSetTimer(){// Create a timer with a two second interval.
aTimer =newSystem.Timers.Timer(2000);// Hook up the Elapsed event for the timer.
aTimer.Elapsed+=OnTimedEvent;
aTimer.AutoReset=true;
aTimer.Enabled=true;}privatestaticvoidOnTimedEvent(Object source,ElapsedEventArgs e){Console.WriteLine("The Elapsed event was raised at {0:HH:mm:ss.fff}",
e.SignalTime);}
var myTimer =newTimer((e)=>{// Code},null,TimeSpan.Zero,TimeSpan.FromSeconds(5));
mais il s'est arrêté en continu après ~ 20 minutes.
Avec ça, j'ai essayé le réglage des solutions
GC.KeepAlive(myTimer)
ou
for(;;){}}
mais ils n'ont pas fonctionné dans mon cas.
Suivant la documentation Microsoft, cela a parfaitement fonctionné:
using System;
using System.Timers;publicclassExample{privatestaticTimer aTimer;publicstaticvoidMain(){// Create a timer and set a two second interval.
aTimer =newSystem.Timers.Timer();
aTimer.Interval=2000;// Hook up the Elapsed event for the timer.
aTimer.Elapsed+=OnTimedEvent;// Have the timer fire repeated events (true is the default)
aTimer.AutoReset=true;// Start the timer
aTimer.Enabled=true;Console.WriteLine("Press the Enter key to exit the program at any time... ");Console.ReadLine();}privatestaticvoidOnTimedEvent(Object source,System.Timers.ElapsedEventArgs e){Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);}}// The example displays output like the following: // Press the Enter key to exit the program at any time... // The Elapsed event was raised at 5/20/2015 8:48:58 PM // The Elapsed event was raised at 5/20/2015 8:49:00 PM // The Elapsed event was raised at 5/20/2015 8:49:02 PM // The Elapsed event was raised at 5/20/2015 8:49:04 PM // The Elapsed event was raised at 5/20/2015 8:49:06 PM
Réponses:
C'est très bien, cependant, pour simuler le temps qui passe, nous devons exécuter une commande qui prend du temps et c'est très clair dans le deuxième exemple.
Cependant, le style d'utilisation d'une boucle for pour faire certaines fonctionnalités prend pour toujours beaucoup de ressources de l'appareil et à la place, nous pouvons utiliser le Garbage Collector pour faire quelque chose comme ça.
On peut voir cette modification dans le code du même livre CLR Via C # Third Ed.
la source
GC.Collect()
. Il n'y a rien à collecter. Cela aurait du sens, siGC.KeepAlive(t)
était appelé aprèsConsole.ReadLine();
Utilisez la classe System.Threading.Timer.
System.Windows.Forms.Timer est principalement conçu pour être utilisé dans un seul thread, 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 ne s'agit que d'un wrapper autour de System.Threading.Timer de toute façon.
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'une minuterie pour s'exécuter périodiquement. Cela évitera éventuellement un garbage collection prématuré de votre objet timer.
Voici un exemple de minuterie dans une application console:
Extrait du livre CLR Via C # de Jeff Richter. En passant, ce livre décrit la logique derrière les 3 types de minuteries dans le chapitre 23, fortement recommandé.
la source
Voici le code pour créer un simple tick de minuterie d'une seconde:
Et voici le résultat obtenu:
EDIT: Ce n'est jamais une bonne idée d'ajouter des boucles hard spin dans le code car elles consomment des cycles CPU sans gain. Dans ce cas, cette boucle a été ajoutée juste pour empêcher l'application de se fermer, 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 Sleep a été ajouté à cette boucle.
la source
Permet de s'amuser un peu
la source
Ou en utilisant Rx, court et doux:
la source
Vous pouvez également utiliser vos propres mécanismes de chronométrage 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 un minuteur. Utilisez ceci si vous avez besoin de contrôler le thread de synchronisation réel:
serait votre fil de synchronisation (modifiez-le pour qu'il s'arrête lorsque cela est nécessaire, et à n'importe quel intervalle de temps souhaité).
et pour utiliser / démarrer, vous pouvez faire:
Le rappel est votre méthode sans paramètre void que vous souhaitez appeler à chaque intervalle. Par exemple:
la source
Vous pouvez également créer le vôtre (si vous n'êtes pas satisfait des options disponibles).
Créer votre propre
Timer
implémentation est assez basique.Ceci est un exemple pour une application qui avait besoin d'un accès aux objets COM sur le même thread que le reste de ma base de code.
Vous pouvez ajouter des événements comme suit:
Pour vous assurer que la minuterie fonctionne, vous devez créer une boucle sans fin comme suit:
la source
Dans C # 5.0+ et .NET Framework 4.5+, vous pouvez utiliser async / await:
la source
doc
Voilà :)
la source
Je vous suggère de suivre les directives Microsoft ( https://docs.microsoft.com/en-us/dotnet/api/system.timers.timer.interval?view=netcore-3.1 ).
J'ai d'abord essayé d'utiliser
System.Threading;
avecmais il s'est arrêté en continu après ~ 20 minutes.
Avec ça, j'ai essayé le réglage des solutions
ou
mais ils n'ont pas fonctionné dans mon cas.
Suivant la documentation Microsoft, cela a parfaitement fonctionné:
la source