.NET, evento cada minuto (en el minuto). ¿Es un temporizador la mejor opción?

Quiero hacer cosas cada minuto minuto a minuto en una aplicación de formularios de Windows usando c #. Me pregunto cuál es la mejor manera de hacerlo.

Podría usar un temporizador y establecer su intervalo en 60000, pero para que funcione en el minuto, tendría que habilitarlo en el minuto exacto, no realmente viable.

Podría usar un temporizador y establecer su intervalo en 1000. Luego, dentro de su evento tick, podría verificar los relojes del minuto actual contra una variable que configuré, si el minuto ha cambiado y luego ejecutar mi código. Esto me preocupa porque hago que mi computadora haga un chequeo cada 1 segundo para poder trabajar cada 1 minuto. ¿Seguro que esto es feo?

Estoy usando formularios de Windows y .Net 2.0, así que no quiero usar el DispatchTimer que viene con .Net 3.5

Este debe ser un problema bastante común. ¿Alguno de ustedes tiene una mejor manera de hacer esto?

Basándose en la respuesta de Aquinas que puede derivar y que no marca exactamente en el minuto dentro de un segundo del minuto:

static System.Timers.Timer t; static void Main(string[] args) { t = new System.Timers.Timer(); t.AutoReset = false; t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed); t.Interval = GetInterval(); t.Start(); Console.ReadLine(); } static double GetInterval() { DateTime now = DateTime.Now; return ((60 - now.Second) * 1000 - now.Millisecond); } static void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { Console.WriteLine(DateTime.Now.ToString("o")); t.Interval = GetInterval(); t.Start(); } 

En mi caja, este código se ajusta de manera consistente dentro de los .02s de cada minuto:

 2010-01-15T16:42:00.0040001-05:00 2010-01-15T16:43:00.0014318-05:00 2010-01-15T16:44:00.0128643-05:00 2010-01-15T16:45:00.0132961-05:00 

Qué tal si:

 int startin = 60 - DateTime.Now.Second; var t = new System.Threading.Timer(o => Console.WriteLine("Hello"), null, startin * 1000, 60000); 

Crear un control de Timer que se active cada 1 segundo (y generalmente no hace más que una simple comprobación) agregará una sobrecarga insignificante a su aplicación.

Simplemente compare el valor de Environment.TickCount o DateTime.Now con el último tiempo almacenado (el anterior ‘minute tick’), y debe tener una solución razonablemente precisa. La resolución de estos dos valores de tiempo es de aproximadamente 15 ms, lo que debería ser suficiente para sus propósitos.

Sin embargo, tenga en cuenta que el intervalo del control del Timer no garantiza que sea tan preciso ni siquiera en ningún lugar ahora, ya que se ejecuta en el bucle de mensajes de Windows, que está vinculado con la capacidad de respuesta de la interfaz de usuario. Nunca confíe en ella ni siquiera para un tiempo moderadamente preciso, aunque es lo suficientemente bueno para disparar eventos repetidos en los que puede verificar el tiempo usando un método más sensible como uno de los dos dados anteriormente.

Puede conectar esto con extensiones reactivas que se ocuparán de muchos problemas relacionados con el temporizador (cambios de reloj, hibernación de la aplicación, etc.). Usa el paquete Nuget Rx-Main y un código como este:

 Action work = () => Console.WriteLine(DateTime.Now.ToLongTimeString()); Scheduler.Default.Schedule( // start in so many seconds TimeSpan.FromSeconds(60 - DateTime.Now.Second), // then run every minute () => Scheduler.Default.SchedulePeriodic(TimeSpan.FromMinutes(1), work)); Console.WriteLine("Press return."); Console.ReadLine(); 

Lea aquí (busque “Introducción a ISchedulerPeriodic”) para ver todos los problemas de los que se está ocupando: http://blogs.msdn.com/b/rxteam/archive/2012/06/20/reactive-extensions-v2-0 -release-candidate-available-now.aspx

Escribí esta clase utilizando el WPF DispatcherTimer pero puede intercambiar el despachador por cualquier temporizador que permita cambiar cuando se despierte desde el estado de suspensión.

La clase se construye con un paso de tiempo fijo y admite inicio / detención / reinicio, inicio / detención / inicio funciona como una operación de reanudación. El cronómetro es como un cronómetro en ese sentido.

Una implementación de reloj simplemente crearía la clase con un intervalo de 1 segundo y escucharía el evento. Tenga cuidado de que este es un reloj en tiempo real, si el evento tick tarda más tiempo que el intervalo para terminar, notará que el reloj intentará alcanzar en tiempo real esto provocará una explosión de eventos tic que se plantean.

 public class FixedStepDispatcherTimer { ///  /// Occurs when the timer interval has elapsed. ///  public event EventHandler Tick; DispatcherTimer timer; public bool IsRunning { get { return timer.IsEnabled; } } long step, nextTick, n; public TimeSpan Elapsed { get { return new TimeSpan(n * step); } } public FixedStepDispatcherTimer(TimeSpan interval) { if (interval < TimeSpan.Zero) { throw new ArgumentOutOfRangeException("interval"); } this.timer = new DispatcherTimer(); this.timer.Tick += new EventHandler(OnTimerTick); this.step = interval.Ticks; } TimeSpan GetTimerInterval() { var interval = nextTick - DateTime.Now.Ticks; if (interval > 0) { return new TimeSpan(interval); } return TimeSpan.Zero; // yield } void OnTimerTick(object sender, EventArgs e) { if (DateTime.Now.Ticks >= nextTick) { n++; if (Tick != null) { Tick(this, EventArgs.Empty); } nextTick += step; } var interval = GetTimerInterval(); Trace.WriteLine(interval); timer.Interval = interval; } public void Reset() { n = 0; nextTick = 0; } public void Start() { var now = DateTime.Now.Ticks; nextTick = now + (step - (nextTick % step)); timer.Interval = GetTimerInterval(); timer.Start(); } public void Stop() { timer.Stop(); nextTick = DateTime.Now.Ticks % step; } } 

Ejecutar un poco de código para ver si el minuto ha cambiado una vez por segundo no debería requerir mucho tiempo de CPU y debería ser aceptable.

¿Qué pasa con Quartz.NET ? Creo que es un buen marco para hacer acciones cronometradas.

Crea un método o coloca este código donde quieras que comience el temporizador:

  int time = 60 - DateTime.Now.Second; // Gets seconds to next minute refreshTimer.Interval = time * 1000; refreshTimer.Start(); 

Y luego, en su evento marcar, establezca el intervalo en 60000:

  private void refreshTimer_Tick(object sender, EventArgs e) { refreshTimer.Interval = 60000; // Sets interval to 60 seconds // Insert Refresh logic } 

Al usar ReactiveExtensions, podría usar el siguiente código si estuviera interesado en hacer algo tan simple como imprimir en la consola.

 using System; using System.Reactive.Linq; namespace ConsoleApplicationExample { class Program { static void Main() { Observable.Interval(TimeSpan.FromMinutes(1)) .Subscribe(_ => { Console.WriteLine(DateTime.Now.ToString()); }); Console.WriteLine(DateTime.Now.ToString()); Console.ReadLine(); } } } 

Puede configurar dos temporizadores. Un temporizador de intervalo corto inicial (quizás para disparar cada segundo, pero depende de cómo presice el segundo temporizador debe disparar en el minuto).

Dispara el temporizador de intervalo corto solo hasta que se alcanza el tiempo de inicio deseado del temporizador de intervalo principal. Una vez que se alcanza el tiempo inicial, se puede activar el segundo temporizador de intervalo principal, y el temporizador de intervalo corto se puede desactivar.

 void StartTimer() { shortIntervalTimer.Interval = 1000; mainIntervalTimer.Interval = 60000; shortIntervalTimer.Tick += new System.EventHandler(this.shortIntervalTimer_Tick); mainIntervalTimer.Tick += new System.EventHandler(mainIntervalTimer_Tick); shortIntervalTimer.Start(); } private void shortIntervalTimer_Tick(object sender, System.EventArgs e) { if (DateTime.Now.Second == 0) { mainIntervalTimer.Start(); shortIntervalTimer.Stop(); } } private void mainIntervalTimer_Tick(object sender, System.EventArgs e) { // do what you need here // } 

Alternativamente, podría dormir para pausar la ejecución hasta que se agote el tiempo que debería estar cerca de su tiempo deseado. Esto solo activará la computadora cuando termine la suspensión, por lo que le ahorrará tiempo de CPU y permitirá que la CPU se apague entre los eventos de procesamiento.

Esto tiene la ventaja de modificar el tiempo de espera para que no se desplace.

 int timeout = 0; while (true) { timeout = (60 - DateTime.Now.Seconds) * 1000 - DateTime.Now.Millisecond; Thread.Sleep(timeout); // do your stuff here } 

Use un temporizador configurado para ejecutarse cada segundo (o milisegundo, cualquiera que sea su umbral de precisión), y luego codifique el método para ejecutar su funcionalidad si y solo si la hora actual está dentro de ese umbral después del punto “en el minuto”.

Lo que estoy usando para las tareas progtwigdas es un System.Threading.Timer (System.Threading.TimerCallback, object, int, int) con la callback establecida en el código que deseo ejecutar en función del intervalo que se proporciona en milisegundos para el valor del período

¿Qué pasa con una combinación de respuesta de Aquinas y ‘encuesta’: (disculpas por la mezcla de idiomas)

 def waitForNearlyAMinute: secsNow = DateTime.Now.Second; waitFor = 55 - secsNow; setupTimer(waitFor, pollForMinuteEdge) def pollForMinuteEdge: if (DateTime.Now.Second == 0): print "Hello, World!"; waitForNearlyAMinute(); else: setupTimer(0.5, pollForMinuteEdge)