Achtung: Die Antworten hier haben einen Fehler, das Timer-Objekt wird Müll sammeln. Der Verweis auf den Timer muss in einer statischen Variablen gespeichert werden, um sicherzustellen, dass er weiter tickt.
Hans Passant
@HansPassant Sie scheinen die klare Aussage in meiner Antwort übersehen zu haben: "Es wird auch empfohlen, immer ein statisches (in VB.NET freigegebenes) System.Threading.Timer zu verwenden, wenn Sie einen Windows-Dienst entwickeln und einen Timer benötigen, der regelmäßig ausgeführt wird Dadurch wird möglicherweise eine vorzeitige Speicherbereinigung Ihres Timer-Objekts vermieden. " Wenn Leute ein zufälliges Beispiel kopieren und blind verwenden wollen, ist das ihr Problem.
Ash
Antworten:
120
Das ist sehr schön, aber um einige Zeit zu simulieren, müssen wir einen Befehl ausführen, der einige Zeit in Anspruch nimmt, und das ist im zweiten Beispiel sehr klar.
Der Stil, eine for-Schleife zu verwenden, um einige Funktionen für immer auszuführen, erfordert jedoch viele Geräteressourcen. Stattdessen können wir den Garbage Collector verwenden, um so etwas zu tun.
Wir können diese Änderung im Code aus demselben Buch CLR Via C # Third Ed sehen.
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, das war sehr hilfreich. Vielen Dank. Die console.readline () und das GC.Collect waren genau das, was ich brauchte.
Seth Spearman
9
@ Ralph Willgoss, Warum GC.Collect (); Wird benötigt?
Puchacz
2
@Puchacz Ich sehe keinen Anlaufpunkt GC.Collect(). Es gibt nichts zu sammeln. Es wäre sinnvoll, wenn GC.KeepAlive(t)nachConsole.ReadLine();
Newprint
1
Es endete nach dem ersten Rückruf
BadPiggie
1
@Khalid Al Hajami "Der Stil, eine for-Schleife zu verwenden, um einige Funktionen für immer auszuführen, erfordert jedoch eine Menge Geräteressourcen. Stattdessen können wir den Garbage Collector verwenden, um so etwas zu tun." Das ist absolut unsinniger Müll. Der Müllsammler ist völlig irrelevant. Haben Sie dies aus einem Buch kopiert und nicht verstanden, was Sie kopiert haben?
Ash
65
Verwenden Sie die System.Threading.Timer-Klasse.
System.Windows.Forms.Timer wurde hauptsächlich für die Verwendung in einem einzelnen Thread entwickelt, normalerweise dem Windows Forms-UI-Thread.
Es gibt auch eine System.Timers-Klasse, die zu Beginn der Entwicklung des .NET-Frameworks hinzugefügt wurde. Es wird jedoch generell empfohlen, stattdessen die System.Threading.Timer-Klasse zu verwenden, da dies ohnehin nur ein Wrapper um System.Threading.Timer ist.
Es wird außerdem empfohlen, immer ein statisches (in VB.NET freigegebenes) System.Threading.Timer zu verwenden, wenn Sie einen Windows-Dienst entwickeln und einen Timer benötigen, der regelmäßig ausgeführt wird. Dadurch wird möglicherweise eine vorzeitige Speicherbereinigung Ihres Timer-Objekts vermieden.
Hier ist ein Beispiel für einen Timer in einer Konsolenanwendung:
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 }}
Aus dem Buch CLR Via C # von Jeff Richter. Übrigens beschreibt dieses Buch die Gründe für die drei Arten von Timern in Kapitel 23, die sehr zu empfehlen sind.
Eric, ich habe es nicht ausprobiert, wäre aber nicht ungewöhnlich, wenn es ein Problem damit gäbe. Ich stelle fest, dass es auch versucht, eine Art Inter-Thread-Synchronisation durchzuführen. Dies ist immer ein Bereich, der schwierig sein kann, es richtig zu machen. Wenn Sie dies in Ihrem Design vermeiden können, ist es immer klug, dies zu tun.
Ash
1
Ash - Ich stimme definitiv mit MSDN-Beispielen überein. Ich würde den Synchronisationscode jedoch nicht sofort rabattieren. Wenn der Timmer in einem eigenen Thread ausgeführt wird, schreiben Sie eine Multithread-App und müssen sich der Probleme im Zusammenhang mit der Synchronisation bewusst sein.
Eric Tuttleman
1
Was passiert, wenn mehrere Methoden mit der Signatur des TimerCallback-Delegaten übereinstimmen?
Ozkan
22
Hier ist der Code zum Erstellen eines einfachen Timer-Ticks von einer Sekunde:
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);}}}
BEARBEITEN: Es ist niemals eine gute Idee, Hard-Spin-Schleifen in den Code einzufügen, da diese CPU-Zyklen ohne Gewinn verbrauchen. In diesem Fall wurde diese Schleife hinzugefügt, um das Schließen der Anwendung zu verhindern und die Aktionen des Threads zu beobachten. Aus Gründen der Korrektheit und zur Reduzierung der CPU-Auslastung wurde dieser Schleife ein einfacher Sleep-Aufruf hinzugefügt.
Das for (;;) {} bewirkt eine 100% ige CPU-Auslastung.
Seth Spearman
1
Ist es nicht ziemlich offensichtlich, wenn Sie eine Endlosschleife haben, führt dies zu einer CPU von 100%. Um dies zu beheben, müssen Sie der Schleife lediglich einen Sleep-Aufruf hinzufügen.
Veight
3
Es ist erstaunlich, wie viele Leute darauf fixiert sind, ob die for-Schleife eine while-Schleife sein sollte und warum die CPU auf 100% geht. Sprechen Sie über den Wald vor lauter Bäumen vermissen! Azimut, ich persönlich würde gerne wissen, wie sich eine Weile (1) von der Endlosschleife unterscheidet. Sicherlich werden die Leute, die den CLR-Compiler-Optimierer schreiben, sicherstellen, dass diese beiden Codekonstrukte genau denselben CLR-Code erstellen?
Blake7
1
Ein Grund, warum (1) nicht funktioniert, ist, dass es nicht gültig ist. C #: test.cs (21,20): Fehler CS0031: Der konstante Wert '1' kann nicht in einen 'Bool' konvertiert werden
Blake7
1
Nicht auf meinem Computer (win8.1, i5), nur etwa 20-30%. Welche Art von Computer hatten Sie damals? @ SethSpearman
Shinzou
17
Lass uns ein bisschen Spaß haben
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();}}}
sehr unlesbar und gegen Best Practice. Es sieht fantastisch aus, sollte aber nicht in der Produktion verwendet werden, da einige Leute selbst wtf und poo gehen.
Piotr Kula
2
Reactive Extensions (Rx) wurde seit 2 Jahren nicht mehr aktiv entwickelt. Außerdem sind die Beispiele ohne Kontext und verwirrend. Wenig zu wissende Diagramme oder Flussbeispiele.
James Bailey
4
Sie können auch Ihre eigenen Timing-Mechanismen verwenden, wenn Sie etwas mehr Kontrolle, aber möglicherweise weniger Genauigkeit und mehr Code / Komplexität wünschen, aber ich würde trotzdem einen Timer empfehlen. Verwenden Sie dies jedoch, wenn Sie die Kontrolle über den tatsächlichen Timing-Thread haben müssen:
Wenn ich einen Batch-Job bis zum Ablauf der Zeit ausführen möchte, wäre Ihr Vorschlag hier der beste?
Johan Bresler
1
Sie können auch Ihre eigenen erstellen (wenn Sie mit den verfügbaren Optionen nicht zufrieden sind).
Das Erstellen einer eigenen TimerImplementierung ist ziemlich einfach.
Dies ist ein Beispiel für eine Anwendung, die Zugriff auf COM-Objekte im selben Thread wie der Rest meiner Codebasis benötigt.
/// <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();}}
Um sicherzustellen, dass der Timer funktioniert, müssen Sie eine Endlosschleife wie folgt erstellen:
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));
aber es hörte kontinuierlich nach ~ 20 Minuten auf.
Damit habe ich die Lösungseinstellung ausprobiert
GC.KeepAlive(myTimer)
oder
for(;;){}}
aber in meinem Fall haben sie nicht funktioniert.
Nach der Microsoft-Dokumentation hat es perfekt funktioniert:
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
Antworten:
Das ist sehr schön, aber um einige Zeit zu simulieren, müssen wir einen Befehl ausführen, der einige Zeit in Anspruch nimmt, und das ist im zweiten Beispiel sehr klar.
Der Stil, eine for-Schleife zu verwenden, um einige Funktionen für immer auszuführen, erfordert jedoch viele Geräteressourcen. Stattdessen können wir den Garbage Collector verwenden, um so etwas zu tun.
Wir können diese Änderung im Code aus demselben Buch CLR Via C # Third Ed sehen.
quelle
GC.Collect()
. Es gibt nichts zu sammeln. Es wäre sinnvoll, wennGC.KeepAlive(t)
nachConsole.ReadLine();
Verwenden Sie die System.Threading.Timer-Klasse.
System.Windows.Forms.Timer wurde hauptsächlich für die Verwendung in einem einzelnen Thread entwickelt, normalerweise dem Windows Forms-UI-Thread.
Es gibt auch eine System.Timers-Klasse, die zu Beginn der Entwicklung des .NET-Frameworks hinzugefügt wurde. Es wird jedoch generell empfohlen, stattdessen die System.Threading.Timer-Klasse zu verwenden, da dies ohnehin nur ein Wrapper um System.Threading.Timer ist.
Es wird außerdem empfohlen, immer ein statisches (in VB.NET freigegebenes) System.Threading.Timer zu verwenden, wenn Sie einen Windows-Dienst entwickeln und einen Timer benötigen, der regelmäßig ausgeführt wird. Dadurch wird möglicherweise eine vorzeitige Speicherbereinigung Ihres Timer-Objekts vermieden.
Hier ist ein Beispiel für einen Timer in einer Konsolenanwendung:
Aus dem Buch CLR Via C # von Jeff Richter. Übrigens beschreibt dieses Buch die Gründe für die drei Arten von Timern in Kapitel 23, die sehr zu empfehlen sind.
quelle
Hier ist der Code zum Erstellen eines einfachen Timer-Ticks von einer Sekunde:
Und hier ist die resultierende Ausgabe:
BEARBEITEN: Es ist niemals eine gute Idee, Hard-Spin-Schleifen in den Code einzufügen, da diese CPU-Zyklen ohne Gewinn verbrauchen. In diesem Fall wurde diese Schleife hinzugefügt, um das Schließen der Anwendung zu verhindern und die Aktionen des Threads zu beobachten. Aus Gründen der Korrektheit und zur Reduzierung der CPU-Auslastung wurde dieser Schleife ein einfacher Sleep-Aufruf hinzugefügt.
quelle
Lass uns ein bisschen Spaß haben
quelle
Oder mit Rx, kurz und bündig:
quelle
Sie können auch Ihre eigenen Timing-Mechanismen verwenden, wenn Sie etwas mehr Kontrolle, aber möglicherweise weniger Genauigkeit und mehr Code / Komplexität wünschen, aber ich würde trotzdem einen Timer empfehlen. Verwenden Sie dies jedoch, wenn Sie die Kontrolle über den tatsächlichen Timing-Thread haben müssen:
Dies wäre Ihr Timing-Thread (ändern Sie dies so, dass es bei Bedarf und in jedem gewünschten Zeitintervall stoppt).
und um / start zu verwenden, können Sie tun:
Callback ist Ihre void parameterlose Methode, die Sie in jedem Intervall aufrufen möchten. Beispielsweise:
quelle
Sie können auch Ihre eigenen erstellen (wenn Sie mit den verfügbaren Optionen nicht zufrieden sind).
Das Erstellen einer eigenen
Timer
Implementierung ist ziemlich einfach.Dies ist ein Beispiel für eine Anwendung, die Zugriff auf COM-Objekte im selben Thread wie der Rest meiner Codebasis benötigt.
Sie können Ereignisse wie folgt hinzufügen:
Um sicherzustellen, dass der Timer funktioniert, müssen Sie eine Endlosschleife wie folgt erstellen:
quelle
In C # 5.0+ und .NET Framework 4.5+ können Sie async / await verwenden:
quelle
doc
Hier hast du es :)
quelle
Ich empfehle Ihnen, die Microsoft-Richtlinien zu befolgen ( https://docs.microsoft.com/en-us/dotnet/api/system.timers.timer.interval?view=netcore-3.1 ).
Ich versuchte zunächst mit
System.Threading;
mitaber es hörte kontinuierlich nach ~ 20 Minuten auf.
Damit habe ich die Lösungseinstellung ausprobiert
oder
aber in meinem Fall haben sie nicht funktioniert.
Nach der Microsoft-Dokumentation hat es perfekt funktioniert:
quelle