2009-09-29 51 views
10

我并不是真的在写闹钟应用程序,但它有助于说明我的问题。.Net中的闹钟应用程序

假设我在应用程序中有一个方法,并且我希望在小时(例如,下午7:00,晚上8:00,晚上9:00等)每小时调用此方法。我可能创建一个计时器并将其间隔设置为3600000,但最终这会漂移与系统时钟不同步。或者我可能使用while()循环与Thread.Sleep(n)来定期检查系统时间,并在达到所需的时间时调用方法,但我不喜欢这(Thread.Sleep(n)是一个大代码气味)。

我在找的是.NET中的一些方法,它允许我在将来的DateTime对象和方法委托或事件处理程序中传递,但我一直未能找到任何此类的东西。我怀疑在Win32 API中有一个方法可以做到这一点,但我还没有找到。

+4

你是否想要推出自己的Quartz.Net? http://ustznet.sourceforge.net/ –

+1

@奥斯汀:不,我只是想找一个简单的方法,就像我在最后一段描述的那样,而不必自己推出或使用一些第三方组件。 – MusiGenesis

+1

内置的Windows调度程序有一个可通过PInvoke访问的API http://www.pinvoke.net/default.aspx/netapi32/NetScheduleJobAdd.html – xcud

回答

10

或者,您可以创建一个间隔为1秒的计时器,并每秒检查当前时间,直到达到事件时间,如果是,则提升事件。

你可以做一个简单的包装为:

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; 
} 

用法:

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

请注意上面的代码是非常粗略的,而不是线程安全的。

+0

是的,但那也不是我要找的,对不起。 – MusiGenesis

+4

你在找什么不存在。 – RichardOD

+1

对不起,但你在找什么呢? – Coincoin

1

您可以创建一个具有专用线程的Alarm类,该线程会在指定时间内进入休眠状态,但这将使用Thread.Sleep方法。喜欢的东西:

// 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

哦,'Thread.Sleep(n)'*和一个空的'catch {}'块。 :P – MusiGenesis

+1

确实。更多的是你可以做的原型。最好使用像Jacob描述的计时器。 – JDunkerley

2

你可以每次触发时,像这样简单的复位定时器时间?请参阅http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx

+0

不幸的是,这并不能解决问题。漂移问题是因为计时器在很长时间内不准确(秒表有同样的问题)。 – MusiGenesis

+0

我从来没有听说过。你是以什么为基础的错误要求? – Jacob

+0

@Jacob:不要听我的话 - 自己试试。启动一个计时器(任何种类),让它运行一两天,看看它有多远。 – MusiGenesis

-1

什么System.Timers.Timer类:

/// <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

不,这不会做我想要的任何事情。 – MusiGenesis

8

有趣的是,我实际上遇到了一个非常类似的问题,并在.Net框架中寻找一种方法来处理这种情况。最后,我们最终实现了我们自己的解决方案,它是一个while循环的变体w/Thread.Sleep(n),其中n越小越接近期望的目标时间(实际上是对数,但有一些合理的阈值当你接近目标时间时,你不会使cpu达到最大。)这是一个非常简单的实现,它只是在现在和目标时间之间休眠一半的时间。

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

我怀着极大的成功使用此之前:

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(); 
    } 
} 

我不知道C#的作品,但是vb工作得很好。

使用在VB:

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

然后,只需添加一个事件处理程序SoundAlarm事件。

+1

TimeOfDay是一个VB.Net唯一的事情,并且带有'DoEvents()'调用的'While'循环会在处理器运行时最大化你的处理器。 – MusiGenesis

+0

不知道,但现在我知道了。原来的代码有15秒循环睡眠,但因为你不希望我把它遗漏了。 – Cyclone

+0

为什么你想要一个在线程中运行的方法中的Application.DoEvents(甚至是所有)都超出了我 –