.NET,事件每分钟(分钟).计时器是最好的选择吗?

39 c# timer system.reactive

我希望使用c#在Windows窗体应用程序中按分钟(按时钟)每分钟做一些事情.我只是想知道最好的方法是什么?

我可以使用一个计时器并将其间隔设置为60000,但为了让它在一分钟内运行,我必须准确地启用它,而不是真的可行.

我可以使用一个计时器并将其间隔设置为1000.然后在其tick事件中,我可以根据我设置的变量检查当前时钟,如果分钟已经改变,则运行我的代码.这让我很担心,因为我让我的电脑每1秒做一次检查,以便每1分钟进行一次工作.当然这很难看?

我正在使用Windows窗体和.Net 2.0,因此不想使用.Net 3.5附带的DispatchTimer

这一定是一个相当普遍的问题.你有没有更好的方法来做到这一点?

Jar*_*red 51

基于aquinas的答案,它可以漂移,并且不会在一分钟内在一分钟内准确地打勾:

static System.Timers.Timer t;

static void Main(string[] args)
{
    t = new System.Timers.Timer();
    t.AutoReset = false;
    t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
    t.Interval = GetInterval();
    t.Start();
    Console.ReadLine();
}

static double GetInterval()
{
    DateTime now = DateTime.Now;
    return ((60 - now.Second) * 1000 - now.Millisecond);
}

static void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    Console.WriteLine(DateTime.Now.ToString("o"));
    t.Interval = GetInterval();
    t.Start();
}
Run Code Online (Sandbox Code Playgroud)

在我的方框中,此代码在每分钟的0.02秒内始终如一:

2010-01-15T16:42:00.0040001-05:00
2010-01-15T16:43:00.0014318-05:00
2010-01-15T16:44:00.0128643-05:00
2010-01-15T16:45:00.0132961-05:00
Run Code Online (Sandbox Code Playgroud)

  • 经过可能会在00s之前稍微发生,这将触发两次.更正:`return((now.Second> 30?120:60) - now.Second)*1000 - now.Millisecond;` (7认同)
  • 为什么投票失败?我认为这是一个非常好的解决方案,即使在数天或数周之后也会非常精确! (2认同)

aqu*_*nas 29

怎么样:

int startin = 60 - DateTime.Now.Second;
var t = new System.Threading.Timer(o => Console.WriteLine("Hello"), 
     null, startin * 1000, 60000);
Run Code Online (Sandbox Code Playgroud)

  • +1:这是处理60秒计时器漂移的好方法. (2认同)
  • 它不处理漂移...漂移约为 8 秒内 -1 毫秒(在我的机器上)。所以必须有一种方法来重新同步它。 (2认同)

Nol*_*rin 8

创建一个Timer每1秒触发一次的控件(通常只做一个简单的检查)将为您的应用程序增加可忽略的开销.

简单地比较的值Environment.TickCount或者DateTime.Now最后存储的时间(之前的"剔分钟"),你应该有一个合理的精确的解决方案.这两个时间值的分辨率约为15毫秒,应该足以满足您的需要.

但请注意,Timer控件的间隔不能保证精确甚至现在在任何地方,因为它在Windows消息循环上运行,这与UI的响应性相关.永远不要依赖它来进行适度精确的计时 - 尽管它足以用于触发重复事件,您可以使用更敏感的方法(例如上面给出的两种方法之一)来检查时间.


Jam*_*rld 7

您可以使用响应式扩展来解决这个问题,这将为您解决许多与计时器相关的问题(时钟更改,应用程序休眠等).使用Nuget包Rx-Main和代码如下:

Action work = () => Console.WriteLine(DateTime.Now.ToLongTimeString());

Scheduler.Default.Schedule(
    // start in so many seconds
    TimeSpan.FromSeconds(60 - DateTime.Now.Second), 
    // then run every minute
    () => Scheduler.Default.SchedulePeriodic(TimeSpan.FromMinutes(1), work));               

Console.WriteLine("Press return.");
Console.ReadLine();
Run Code Online (Sandbox Code Playgroud)

请阅读此处(搜索"ISchedulerPeriodic简介")以查看此问题所涉及的所有问题:http://blogs.msdn.com/b/rxteam/archive/2012/06/20/reactive-extensions-v2-0 -release候补可供now.aspx


Joh*_*ren 5

我jsut使用WPF DispatcherTimer编写了这个类,但您可以将调度程序交换为支持从睡眠状态唤醒时更改的任何计时器.

该类具有固定的时间步长并支持启动/停止/复位,启动/停止/启动就像恢复操作一样.在这方面,计时器就像秒表.

时钟实现只是创建一个间隔为1秒的类并监听事件.虽然这是一个实时时钟,但要小心,如果tick事件花费的时间超过完成时间间隔,你会发现时钟会尝试赶上实时,这将导致一系列的tick事件被提升.

public class FixedStepDispatcherTimer
{
    /// <summary>
    /// Occurs when the timer interval has elapsed.
    /// </summary>
    public event EventHandler Tick;

    DispatcherTimer timer;

    public bool IsRunning { get { return timer.IsEnabled; } }

    long step, nextTick, n;

    public TimeSpan Elapsed { get { return new TimeSpan(n * step); } }

    public FixedStepDispatcherTimer(TimeSpan interval)
    {
        if (interval < TimeSpan.Zero)
        {
            throw new ArgumentOutOfRangeException("interval");
        }
        this.timer = new DispatcherTimer();
        this.timer.Tick += new EventHandler(OnTimerTick);
        this.step = interval.Ticks;
    }

    TimeSpan GetTimerInterval()
    {
        var interval = nextTick - DateTime.Now.Ticks;
        if (interval > 0)
        {
            return new TimeSpan(interval);
        }
        return TimeSpan.Zero; // yield
    }

    void OnTimerTick(object sender, EventArgs e)
    {
        if (DateTime.Now.Ticks >= nextTick)
        {
            n++;
            if (Tick != null)
            {
                Tick(this, EventArgs.Empty);
            }
            nextTick += step;
        }
        var interval = GetTimerInterval();
        Trace.WriteLine(interval);
        timer.Interval = interval;
    }

    public void Reset()
    {
        n = 0;
        nextTick = 0;
    }

    public void Start()
    {
        var now = DateTime.Now.Ticks;
        nextTick = now + (step - (nextTick % step));
        timer.Interval = GetTimerInterval();
        timer.Start();
    }

    public void Stop()
    {
        timer.Stop();
        nextTick = DateTime.Now.Ticks % step;
    }
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*own 5

创建一个方法或将此代码放在您希望计时器启动的位置:

 int time = 60 - DateTime.Now.Second; // Gets seconds to next minute
        refreshTimer.Interval = time * 1000;
        refreshTimer.Start();
Run Code Online (Sandbox Code Playgroud)

然后在您的滴答事件上将间隔设置为 60000:

  private void refreshTimer_Tick(object sender, EventArgs e)
    {
        refreshTimer.Interval = 60000; // Sets interval to 60 seconds
        // Insert Refresh logic
    }
Run Code Online (Sandbox Code Playgroud)


Wal*_*t W 1

运行一些代码来查看分钟是否每秒更改一次应该不需要太多 CPU 时间,并且应该是可以接受的。