.Net中的闹钟应用程序

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

假设我的应用程序中有一个方法,我希望每小时每小时调用一次该方法(例如,晚上7点,晚上8点,晚上9点等)。 我可以创建一个Timer并将其Interval设置为3600000,但最终这会与系统时钟失去同步。 或者我可以使用带有Thread.Sleep(n)while()循环来定期检查系统时间并在达到所需时间时调用方法,但我也不喜欢这样( Thread.Sleep(n)是一个大代码闻到我的味道)。

我正在寻找的是.Net中的一些方法,它允许我传入未来的DateTime对象和方法委托或事件处理程序,但我还没有找到任何这样的东西。 我怀疑在Win32 API中有一个方法可以做到这一点,但我也无法找到它。

或者,您可以创建一个间隔为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!"); 

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

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

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

您可以在每次触发时重置计时器持续时间,如下所示:

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

您可以创建一个具有专用线程的Alarm类,该线程将在指定时间内进入hibernate状态,但这将使用Thread.Sleep方法。 就像是:

 ///  /// Alarm Class ///  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(); } ///  /// Alarm Event ///  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); } } } 

那么System.Timers.Timer类呢? 请参阅http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx

我之前使用过这个并取得了巨大的成功:

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事件添加事件处理程序。