2009-09-29 83 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(甚至是所有)都超出了我 –