2009-09-29 21 views
10

No estoy realmente escribiendo una aplicación de reloj de alarma, pero ayudará a ilustrar mi pregunta.Aplicación de reloj despertador en .Net

Digamos que tengo un método en mi aplicación, y quiero que este método se llame cada hora en la hora (por ejemplo, a las 7:00 p.m., 8:00 p.m., 9:00 p.m., etc.). I podría crear un temporizador y configurar su intervalo en 3600000, pero eventualmente esto se saldría de sincronización con el reloj del sistema. O I podría utilizar un while() bucle con Thread.Sleep(n) para comprobar periódicamente la hora del sistema y llamar al método cuando se alcanza el tiempo deseado, pero tampoco me gusta esto (Thread.Sleep(n) es un gran olor a código para mí).

Lo que estoy buscando es algún método en .Net que me permita pasar en un futuro objeto DateTime y un método delegado o manejador de eventos, pero no he podido encontrar tal cosa. Sospecho que hay un método en la API de Win32 que hace esto, pero tampoco he podido encontrarlo.

+4

¿Está buscando rodar su propio Quartz.Net? http://quartznet.sourceforge.net/ –

+1

@Austin: no, solo estoy buscando un método simple como el que describí en el último párrafo, sin tener que mover el mío o usar un componente de terceros. – MusiGenesis

+1

El programador de Windows incorporado tiene una API accesible a través de PInvoke http://www.pinvoke.net/default.aspx/netapi32/NetScheduleJobAdd.html – xcud

Respuesta

10

O bien, puede crear un temporizador con un intervalo de 1 segundo y verificar la hora actual cada segundo hasta que se llegue a la hora del evento, si es así, levanta su evento.

Usted puede hacer un simple envoltorio para ello:

public class AlarmClock 
{ 
    public AlarmClock(DateTime alarmTime) 
    { 
     this.alarmTime = alarmTime; 

     timer = new Timer(); 
     timer.Elapsed += timer_Elapsed; 
     timer.Interval = 1000; 
     timer.Start(); 

     enabled = true; 
    } 

    void timer_Elapsed(object sender, ElapsedEventArgs e) 
    { 
     if(enabled && DateTime.Now > alarmTime) 
     { 
      enabled = false; 
      OnAlarm(); 
      timer.Stop(); 
     } 
    } 

    protected virtual void OnAlarm() 
    { 
     if(alarmEvent != null) 
      alarmEvent(this, EventArgs.Empty); 
    } 


    public event EventHandler Alarm 
    { 
     add { alarmEvent += value; } 
     remove { alarmEvent -= value; } 
    } 

    private EventHandler alarmEvent; 
    private Timer timer; 
    private DateTime alarmTime; 
    private bool enabled; 
} 

Uso:

AlarmClock clock = new AlarmClock(someFutureTime); 
clock.Alarm += (sender, e) => MessageBox.Show("Wake up!"); 

Tenga en cuenta que el código anterior es muy esquemática y no seguro para subprocesos.

+0

Sí, pero eso tampoco es lo que estoy buscando, lo siento. – MusiGenesis

+4

Lo que estás buscando no existe. – RichardOD

+1

Lo sentimos pero, ¿qué estás buscando, entonces? – Coincoin

1

Puede crear una clase de alarma que tenga una secuencia dedicada que se suspenda hasta la hora especificada, pero utilizará el método Thread.Sleep. Algo así como:

/// <summary> 
/// Alarm Class 
/// </summary> 
public class Alarm 
{ 
    private TimeSpan wakeupTime; 

    public Alarm(TimeSpan WakeUpTime) 
    { 
     this.wakeupTime = WakeUpTime; 
     System.Threading.Thread t = new System.Threading.Thread(TimerThread) { IsBackground = true, Name = "Alarm" }; 
     t.Start(); 
    } 

    /// <summary> 
    /// Alarm Event 
    /// </summary> 
    public event EventHandler AlarmEvent = delegate { }; 

    private void TimerThread() 
    { 
     DateTime nextWakeUp = DateTime.Today + wakeupTime; 
     if (nextWakeUp < DateTime.Now) nextWakeUp = nextWakeUp.AddDays(1.0); 

     while (true) 
     { 
      TimeSpan ts = nextWakeUp.Subtract(DateTime.Now); 

      System.Threading.Thread.Sleep((int)ts.TotalMilliseconds); 

      try { AlarmEvent(this, EventArgs.Empty); } 
      catch { } 

      nextWakeUp = nextWakeUp.AddDays(1.0); 
     } 
    } 
} 
+0

Ooh, 'Thread.Sleep (n)' * y * un bloque 'catch {}' vacío. : P – MusiGenesis

+1

De hecho. Más un prototipo de lo que podrías hacer. Es mejor usar un temporizador como lo describe Jacob. – JDunkerley

2

Simplemente podría restablecer el temporizador de duración cada vez que se dispara, como esto:

// using System.Timers; 

private void myMethod() 
{ 
    var timer = new Timer { 
     AutoReset = false, Interval = getMillisecondsToNextAlarm() }; 
    timer.Elapsed += (src, args) => 
    { 
     // Do timer handling here. 

     timer.Interval = getMillisecondsToNextAlarm(); 
     timer.Start(); 
    }; 
    timer.Start(); 
} 

private double getMillisecondsToNextAlarm() 
{ 
    // This is an example of making the alarm go off at every "o'clock" 
    var now = DateTime.Now; 
    var inOneHour = now.AddHours(1.0); 
    var roundedNextHour = new DateTime(
     inOneHour.Year, inOneHour.Month, inOneHour.Day, inOneHour.Hour, 0, 0); 
    return (roundedNextHour - now).TotalMilliseconds; 
} 
+0

Esto no resolvería el problema, desafortunadamente. El problema de la deriva se debe a que el temporizador es inexacto durante largas duraciones (el cronómetro tiene el mismo problema). – MusiGenesis

+0

Nunca había escuchado eso antes. ¿En qué basa el reclamo de inexactitud? – Jacob

+0

@Jacob: no confíe en mi palabra, pruébelo usted mismo. Comience un temporizador (cualquier variedad) y déjelo funcionar durante un día o dos, y vea qué tan lejos se pone. – MusiGenesis

8

Interesante, me he encontrado con un problema muy similar y fui en busca de un método en el .Net framework que manejaría este escenario. Al final, terminamos implementando nuestra propia solución que era una variación en un ciclo while w/Thread.Sleep (n) donde n se hace más pequeño cuanto más se acerca al tiempo objetivo deseado (logarítmicamente en realidad, pero con algunos umbrales razonables para no estás maximizando la CPU cuando te acercas al tiempo objetivo.) Aquí hay una implementación realmente simple que simplemente duerme la mitad del tiempo entre ahora y el tiempo objetivo.

class Program 
{ 
    static void Main(string[] args) 
    { 
     SleepToTarget Temp = new SleepToTarget(DateTime.Now.AddSeconds(30),Done); 
     Temp.Start(); 
     Console.ReadLine(); 
    } 

    static void Done() 
    { 
     Console.WriteLine("Done"); 
    } 
} 

class SleepToTarget 
{ 
    private DateTime TargetTime; 
    private Action MyAction; 
    private const int MinSleepMilliseconds = 250; 

    public SleepToTarget(DateTime TargetTime,Action MyAction) 
    { 
     this.TargetTime = TargetTime; 
     this.MyAction = MyAction; 
    } 

    public void Start() 
    { 
     new Thread(new ThreadStart(ProcessTimer)).Start(); 
    } 

    private void ProcessTimer() 
    { 
     DateTime Now = DateTime.Now; 

     while (Now < TargetTime) 
     { 
      int SleepMilliseconds = (int) Math.Round((TargetTime - Now).TotalMilliseconds/2); 
      Console.WriteLine(SleepMilliseconds); 
      Thread.Sleep(SleepMilliseconds > MinSleepMilliseconds ? SleepMilliseconds : MinSleepMilliseconds); 
      Now = DateTime.Now; 
     } 

     MyAction(); 
    } 
} 
-2

he utilizado esto antes con gran éxito:

Vb.net:

Imports System.Threading 
Public Class AlarmClock 
    Public startTime As Integer = TimeOfDay.Hour 
    Public interval As Integer = 1 
    Public Event SoundAlarm() 
    Public Sub CheckTime() 
     While TimeOfDay.Hour < startTime + interval 
      Application.DoEvents() 
     End While 
     RaiseEvent SoundAlarm() 
    End Sub 
    Public Sub StartClock() 
     Dim clockthread As Thread = New Thread(AddressOf CheckTime) 
     clockthread.Start() 
    End Sub 
End Class 

C#:

using System.Threading; 
public class AlarmClock 
{ 
    public int startTime = TimeOfDay.Hour; 
    public int interval = 1; 
    public event SoundAlarmEventHandler SoundAlarm; 
    public delegate void SoundAlarmEventHandler(); 
    public void CheckTime() 
    { 
     while (TimeOfDay.Hour < startTime + interval) { 
      Application.DoEvents(); 
     } 
     if (SoundAlarm != null) { 
      SoundAlarm(); 
     } 
    } 
    public void StartClock() 
    { 
     Thread clockthread = new Thread(CheckTime); 
     clockthread.Start(); 
    } 
} 

no sé si las obras de C#, pero el vb funciona bien.

Uso en VB:

Dim clock As New AlarmClock 
clock.interval = 1 'Interval is in hours, could easily convert to anything else 
clock.StartClock() 

A continuación, sólo tiene que añadir un controlador de eventos para el evento SoundAlarm.

+1

TimeOfDay es solo una cosa de VB.Net, y el bucle 'While' con una llamada' DoEvents() 'maximizará su procesador mientras esto se está ejecutando. – MusiGenesis

+0

No lo sabía, pero ahora lo sé. El código original tenía el loop sleep por 15 segundos, pero como no querías, lo dejé. – Cyclone

+0

¿Por qué querría Application.DoEvents en un método que se ejecuta en un hilo (o incluso en absoluto) me supera? –

Cuestiones relacionadas