C# .Net中的闹钟应用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1493203/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 18:04:59  来源:igfitidea点击:

Alarm clock application in .Net

c#.netdatetimetime

提问by MusiGenesis

I'm not really writing an alarm clock application, but it will help to illustrate my question.

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

Let's say that I have a method in my application, and I want this method to be called every hour on the hour (e.g. at 7:00 PM, 8:00 PM, 9:00 PM etc.). I couldcreate a Timer and set its Interval to 3600000, but eventually this would drift out of sync with the system clock. Or I coulduse a while()loop with Thread.Sleep(n)to periodically check the system time and call the method when the desired time is reached, but I don't like this either (Thread.Sleep(n)is a big code smell for me).

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

What I'm looking for is some method in .Net that lets me pass in a future DateTime object and a method delegate or event handler, but I haven't been able to find any such thing. I suspect there's a method in the Win32 API that does this, but I haven't been able to find that, either.

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

采纳答案by Coincoin

Or, you could create a timer with an interval of 1 second and check the current time every second until the event time is reached, if so, you raise your event.

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

You can make a simple wrapper for that :

您可以为此制作一个简单的包装器:

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

Usage:

用法:

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

Please note the code above is very sketchy and not thread safe.

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

回答by JDunkerley

You could create an Alarm class which has a dedicated thread which goes to sleep until the specified time, but this will use the Thread.Sleep method. Something like:

您可以创建一个具有专用线程的 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);
        }
    }
}

回答by Jacob

You could simply reset the timer duration each time it fires, like this:

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

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

回答by Jacob

回答by Mark A.

Interesting, I've actually come across a very similar issue and went looking for a method in the .Net framework that would handle this scenario. In the end, we ended up implementing our own solution that was a variation on a while loop w/ Thread.Sleep(n) where n gets smaller the closer you get to the desired target time (logarithmically actually, but with some reasonable thresholds so you're not maxing the cpu when you get close to the target time.) Here's a really simple implementation that just sleeps half the time between now and the target time.

有趣的是,我实际上遇到了一个非常相似的问题,并在 .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();
    }
}

回答by Cyclone

I have used this before with great success:

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

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#:

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

I don't know if the c# works, but the vb works just fine.

我不知道 c# 是否工作,但 vb 工作得很好。

Usage in VB:

VB中的用法:

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

Then, just add an event handler for the SoundAlarm event.

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

回答by Steve Cherry

I know it's a bit of an old question, but I came across this when I was looking for an answer to something else. I thought I'd throw my two cents in here, since I recently had this particular issue.

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

Another thing you can do is schedule the method like so:

您可以做的另一件事是像这样安排方法:

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

Bearing in mind it can only wait up to the maximum value of int 32 (somewhere around a month), it should work for your purposes. Usage:

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

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

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

And you should have a console message in an hour.

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