Precaución: las respuestas aquí tienen un error, el objeto Timer se va a recolectar basura. La referencia al temporizador debe almacenarse en una variable estática para garantizar que siga marcando.
Hans Passant
@HansPassant Parece haber perdido la declaración clara en mi respuesta: "También se recomienda usar siempre un System.Threading.Timer estático (compartido en VB.NET) si está desarrollando un servicio de Windows y necesita un temporizador para ejecutarse periódicamente . Esto evitará posiblemente la recolección de basura prematura de su objeto temporizador ". Si la gente quiere copiar un ejemplo aleatorio y usarlo a ciegas, ese es su problema.
Ash
Respuestas:
120
Eso es muy bueno, sin embargo, para simular el paso del tiempo, necesitamos ejecutar un comando que tome algo de tiempo y que esté muy claro en el segundo ejemplo.
Sin embargo, el estilo de usar un bucle for para hacer alguna funcionalidad para siempre requiere muchos recursos del dispositivo y en su lugar podemos usar el recolector de basura para hacer algo así.
Podemos ver esta modificación en el código del mismo libro 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();}}
@ Puchacz No veo un punto de llamar GC.Collect(). No hay nada que coleccionar. Tendría sentido si GC.KeepAlive(t)se llamara despuésConsole.ReadLine();
newprint
1
Terminó después de la primera devolución de llamada
BadPiggie
1
@Khalid Al Hajami "Sin embargo, el estilo de usar un bucle for para hacer alguna funcionalidad para siempre requiere muchos recursos del dispositivo y en su lugar podemos usar el recolector de basura para hacer algo así". Esto es una basura absoluta sin sentido. El recolector de basura es completamente irrelevante. ¿Copió esto de un libro y no entendió lo que estaba copiando?
Ash
65
Use la clase System.Threading.Timer.
System.Windows.Forms.Timer está diseñado principalmente para su uso en un solo subproceso, generalmente el subproceso de interfaz de usuario de Windows Forms.
También hay una clase System.Timers agregada al inicio del desarrollo del marco .NET. Sin embargo, generalmente se recomienda usar la clase System.Threading.Timer en su lugar, ya que de todos modos esto es solo una envoltura alrededor de System.Threading.Timer.
También se recomienda usar siempre un System.Threading.Timer estático (compartido en VB.NET) si está desarrollando un servicio de Windows y necesita un temporizador para ejecutarse periódicamente. Esto evitará la recolección de basura posiblemente prematura de su objeto de temporizador.
Aquí hay un ejemplo de un temporizador en una aplicación de consola:
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 }}
Del libro CLR Via C # de Jeff Richter. Por cierto, este libro describe la justificación detrás de los 3 tipos de temporizadores en el Capítulo 23, muy recomendable.
Eric, no lo he probado, pero no sería inusual si hubiera un problema. Noté que también está tratando de hacer algún tipo de sincronización entre subprocesos, esto siempre es un área que puede ser difícil de hacer bien. Si puede evitarlo en su diseño, siempre es inteligente hacerlo.
Ash
1
Ash: definitivamente estoy de acuerdo con los ejemplos de msdn. Sin embargo, no descartaría de inmediato el código de sincronización, si el temporizador se ejecuta en su propio hilo, entonces está escribiendo una aplicación multiproceso y necesita estar al tanto de los problemas relacionados con la sincronización.
Eric Tuttleman
1
¿Qué sucede si hay varios métodos que coinciden con la firma del delegado TimerCallback?
Ozkan
22
Aquí está el código para crear un simple tic de temporizador de un segundo:
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);}}}
EDITAR: Nunca es una buena idea agregar bucles de giro duro al código, ya que consumen ciclos de CPU sin ganancia. En este caso, ese bucle se agregó solo para detener el cierre de la aplicación, lo que permite observar las acciones del hilo. Pero en aras de la corrección y para reducir el uso de la CPU, se agregó una simple llamada de suspensión a ese bucle.
¿No es bastante obvio si tienes un bucle infinito para que eso dará como resultado una CPU del 100%? Para solucionarlo, todo lo que necesita hacer es agregar una llamada de suspensión al bucle.
veight
3
Es sorprendente cuántas personas están obsesionadas con saber si el ciclo for debería ser un ciclo while y por qué la CPU llega al 100%. ¡Habla de echar de menos la madera para los árboles! Azimut, personalmente me gustaría saber cómo un tiempo (1) sería diferente al bucle infinito. ¿Seguramente las personas que escriben el optimizador del compilador CLR se asegurarán de que estas dos construcciones de código creen exactamente el mismo código CLR?
Blake7
1
Una razón por la que mientras (1) no funcionará es que no es válido c #: test.cs (21,20): error CS0031: El valor constante '1' no se puede convertir en 'bool'
Blake7
1
No en mi máquina (win8.1, i5), solo alrededor del 20-30%, ¿qué tipo de computadora tenía en ese momento? @SethSpearman
shinzou
17
Vamos a divertirnos un poco
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();}}}
Muy ilegible y en contra de las mejores prácticas. Se ve increíble, pero no se debe usar en la producción porque algunas personas se irán y se cagarán.
Piotr Kula
2
Las extensiones reactivas (Rx) no se han desarrollado activamente durante 2 años. Además, los ejemplos son sin contexto y confusos. Poco que saber diagramas o ejemplos de flujo.
James Bailey
4
También puede usar sus propios mecanismos de temporización si desea un poco más de control, pero posiblemente menos precisión y más código / complejidad, pero aún así recomendaría un temporizador. Sin embargo, use esto si necesita tener control sobre el hilo de sincronización real:
Si quiero ejecutar un trabajo por lotes hasta que se agote, ¿su sugerencia aquí sería la mejor?
Johan Bresler
1
También puede crear el suyo propio (si no está satisfecho con las opciones disponibles).
Crear tu propia Timerimplementación es algo bastante básico.
Este es un ejemplo para una aplicación que necesitaba acceso a objetos COM en el mismo hilo que el resto de mi base de código.
/// <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();}}
Para asegurarse de que el temporizador funciona, debe crear un bucle sin fin de la siguiente manera:
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));
pero se detuvo continuamente después de ~ 20 minutos.
Con eso, probé la configuración de soluciones
GC.KeepAlive(myTimer)
o
for(;;){}}
pero no funcionaron en mi caso.
Siguiendo la documentación de Microsoft, funcionó perfectamente:
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
Respuestas:
Eso es muy bueno, sin embargo, para simular el paso del tiempo, necesitamos ejecutar un comando que tome algo de tiempo y que esté muy claro en el segundo ejemplo.
Sin embargo, el estilo de usar un bucle for para hacer alguna funcionalidad para siempre requiere muchos recursos del dispositivo y en su lugar podemos usar el recolector de basura para hacer algo así.
Podemos ver esta modificación en el código del mismo libro CLR Via C # Third Ed.
fuente
GC.Collect()
. No hay nada que coleccionar. Tendría sentido siGC.KeepAlive(t)
se llamara despuésConsole.ReadLine();
Use la clase System.Threading.Timer.
System.Windows.Forms.Timer está diseñado principalmente para su uso en un solo subproceso, generalmente el subproceso de interfaz de usuario de Windows Forms.
También hay una clase System.Timers agregada al inicio del desarrollo del marco .NET. Sin embargo, generalmente se recomienda usar la clase System.Threading.Timer en su lugar, ya que de todos modos esto es solo una envoltura alrededor de System.Threading.Timer.
También se recomienda usar siempre un System.Threading.Timer estático (compartido en VB.NET) si está desarrollando un servicio de Windows y necesita un temporizador para ejecutarse periódicamente. Esto evitará la recolección de basura posiblemente prematura de su objeto de temporizador.
Aquí hay un ejemplo de un temporizador en una aplicación de consola:
Del libro CLR Via C # de Jeff Richter. Por cierto, este libro describe la justificación detrás de los 3 tipos de temporizadores en el Capítulo 23, muy recomendable.
fuente
Aquí está el código para crear un simple tic de temporizador de un segundo:
Y aquí está el resultado resultante:
EDITAR: Nunca es una buena idea agregar bucles de giro duro al código, ya que consumen ciclos de CPU sin ganancia. En este caso, ese bucle se agregó solo para detener el cierre de la aplicación, lo que permite observar las acciones del hilo. Pero en aras de la corrección y para reducir el uso de la CPU, se agregó una simple llamada de suspensión a ese bucle.
fuente
Vamos a divertirnos un poco
fuente
O usando Rx, corto y dulce:
fuente
También puede usar sus propios mecanismos de temporización si desea un poco más de control, pero posiblemente menos precisión y más código / complejidad, pero aún así recomendaría un temporizador. Sin embargo, use esto si necesita tener control sobre el hilo de sincronización real:
sería su hilo de sincronización (modifique esto para detener cuando sea necesario y en el intervalo de tiempo que desee).
y para usar / comenzar puedes hacer:
La devolución de llamada es su método vacío sin parámetros al que desea llamar en cada intervalo. Por ejemplo:
fuente
También puede crear el suyo propio (si no está satisfecho con las opciones disponibles).
Crear tu propia
Timer
implementación es algo bastante básico.Este es un ejemplo para una aplicación que necesitaba acceso a objetos COM en el mismo hilo que el resto de mi base de código.
Puede agregar eventos de la siguiente manera:
Para asegurarse de que el temporizador funciona, debe crear un bucle sin fin de la siguiente manera:
fuente
En C # 5.0+ y .NET Framework 4.5+ puedes usar async / await:
fuente
Doc
Ahí tienes :)
fuente
Le sugiero que siga las pautas de Microsoft ( https://docs.microsoft.com/en-us/dotnet/api/system.timers.timer.interval?view=netcore-3.1 ).
Primero intenté usar
System.Threading;
conpero se detuvo continuamente después de ~ 20 minutos.
Con eso, probé la configuración de soluciones
o
pero no funcionaron en mi caso.
Siguiendo la documentación de Microsoft, funcionó perfectamente:
fuente