.Net中的闹钟应用程序

时间:2009-09-29 15:13:19

标签: c# .net datetime time

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

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

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

7 个答案:

答案 0 :(得分:11)

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

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

答案 1 :(得分:8)

有趣的是,我实际上遇到了一个非常类似的问题,并在.Net框架中寻找可以处理这种情况的方法。最后,我们最终实现了我们自己的解决方案,这是一个带有Thread.Sleep(n)的while循环的变体,其中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 :(得分:2)

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

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

答案 3 :(得分:1)

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

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

答案 4 :(得分:0)

我知道这是一个老问题,但是当我在寻找其他问题的答案时遇到了这个问题。我以为我会在这里扔两分钱,因为我最近有这个特别的问题。

您可以做的另一件事是安排如下方法:

/// Schedule the given action for the given time.
public async void ScheduleAction ( Action action , DateTime ExecutionTime )
{
    try
    {
        await Task.Delay ( ( int ) ExecutionTime.Subtract ( DateTime.Now ).TotalMilliseconds );
        action ( );
    }
    catch ( Exception )
    {
        // Something went wrong
    }
}

请记住,它最多只能等待int 32的最大值(大约一个月左右),它应该可以满足您的目的。用法:

void MethodToRun ( )
{
    Console.WriteLine ("Hello, World!");
}

void CallingMethod ( )
{
    var NextRunTime = DateTime.Now.AddHours(1);
    ScheduleAction ( MethodToRun, NextRunTime );
}

您应该在一个小时内收到控制台消息。

答案 5 :(得分:-1)

答案 6 :(得分:-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事件添加一个事件处理程序。