C# .NET,每分钟一次(按分钟)。定时器是最好的选择吗?

C# .NET,每分钟一次(按分钟)。定时器是最好的选择吗?,c#,winforms,timer,C#,Winforms,Timer,我想用c#在windows窗体应用程序中每分每秒(按时钟)做一些事情。我只是想知道最好的办法是什么 我可以使用计时器并将其间隔设置为60000,但要让它在一分钟内运行,我必须精确地在一分钟内启用它,这不是真正可行的 我可以使用计时器,将其间隔设置为1000。然后在它的tick事件中,我可以对照我设置的变量检查当前分钟的时钟,如果分钟发生了变化,那么运行我的代码。这让我担心,因为我让我的电脑每1秒检查一次,以便每1分钟执行一次工作。这肯定很难看吧 我使用的是windows窗体和.Net 2.0,因

我想用c#在windows窗体应用程序中每分每秒(按时钟)做一些事情。我只是想知道最好的办法是什么

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

我可以使用计时器,将其间隔设置为1000。然后在它的tick事件中,我可以对照我设置的变量检查当前分钟的时钟,如果分钟发生了变化,那么运行我的代码。这让我担心,因为我让我的电脑每1秒检查一次,以便每1分钟执行一次工作。这肯定很难看吧

我使用的是windows窗体和.Net 2.0,因此不想使用.Net 3.5附带的
DispatchTimer


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

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

创建一个每1秒触发一次的控件(通常只做简单的检查)会给应用程序增加微不足道的开销

只需将或的值与上次存储的时间(前一个“分钟刻度”)进行比较,就可以得到一个相当精确的解决方案。这两个时间值的分辨率约为15毫秒,这对于您的目的来说应该足够了


但是,请注意,
计时器的时间间隔
控件的时间间隔不能保证精确到现在的任何位置,因为它在Windows消息循环上运行,这与UI的响应性有关。即使是中等精度的计时,也不要依赖它——尽管它足以触发重复事件,您可以使用更敏感的方法(如上面给出的两种方法之一)检查时间。

使用定时器设置为每秒运行一次(或毫秒,无论精度阈值是多少),然后编写方法,当且仅当当前时间在该阈值内且超过了“分钟”点时运行功能。

我用于计划任务的是System.Threading.Timer(System.Threading.TimerCallback,object,int,int)将回调设置为我希望根据周期值的间隔(以毫秒为单位)执行的代码。

如何:

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

阿奎那的回答和民意调查的结合怎么样?(为混合语言道歉)


你可以设置两个计时器。一个初始的短间隔计时器(可能每秒钟触发一次,但取决于第二个计时器在一分钟内必须触发的频率)

您只能在达到主间隔计时器的所需开始时间之前启动短间隔计时器。一旦达到初始时间,可激活第二个主间隔计时器,并停用短间隔计时器

void StartTimer()
{

  shortIntervalTimer.Interval = 1000;
  mainIntervalTimer.Interval = 60000; 

  shortIntervalTimer.Tick += 
    new System.EventHandler(this.shortIntervalTimer_Tick);
  mainIntervalTimer.Tick += 
    new System.EventHandler(mainIntervalTimer_Tick);

  shortIntervalTimer.Start();

}

private void shortIntervalTimer_Tick(object sender, System.EventArgs e)
{
  if (DateTime.Now.Second == 0)
    {
      mainIntervalTimer.Start();
      shortIntervalTimer.Stop();
    }
}

private void mainIntervalTimer_Tick(object sender, System.EventArgs e)
{
  // do what you need here //
}

那怎么办?我认为这是一个很好的执行定时操作的框架。

或者,您可以通过睡眠暂停执行,直到它超时,这应该接近您想要的时间。这只会在睡眠结束时唤醒计算机,这样可以节省CPU时间,并让CPU在处理事件之间断电

这样做的好处是修改超时,使其不会漂移

int timeout = 0;

while (true)  {
  timeout = (60 - DateTime.Now.Seconds) * 1000 - DateTime.Now.Millisecond;
  Thread.Sleep(timeout);

  // do your stuff here
}

以阿奎那的答案为基础,阿奎那的答案可能会漂移,而且不会在一分钟内准确地滴答作响:

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();
}
在我的方框中,此代码在每分钟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

我jsut使用WPF Dispatchermer编写了这个类,但是您可以将dispatcher替换为任何支持在从睡眠状态唤醒时进行更改的计时器

该类使用固定的时间步长构造,支持启动/停止/重置,启动/停止/启动的工作方式类似于恢复操作。在这方面,计时器就像秒表

时钟实现只需创建间隔为1秒的类并侦听事件。尽管这是一个实时时钟,但要小心,如果滴答声事件的完成时间长于间隔,您会注意到时钟将试图赶上实时,这将导致引发一系列滴答声事件

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;
    }
}
公共类FixedStepDispatcher
{
/// 
///在计时器间隔已过时发生。
/// 
公共事件处理程序;
调度定时器;
public bool正在运行{get{return timer.IsEnabled;}}
长步,下一步,n;
公共时间跨度已过{获取{返回新时间跨度(n*步);}
公共固定步骤分派器(时间跨度间隔)
{
if(间隔<时间间隔0)
{
抛出新ArgumentOutOfRangeException(“interval”);
}
this.timer=new dispatchermer();
this.timer.Tick+=新的EventHandler(OnTimerTick);
this.step=interval.Ticks;
}
TimeSpan GetTimerInterval()
{
var interval=nextTick-DateTime.Now.Ticks;
如果(间隔>0)
{
返回新的时间跨度(间隔);
}
返回TimeSpan.Zero;//收益率
}
void OnTimerTick(对象发送方,事件参数e)
{
如果(DateTime.Now.Ticks>=nextTick)
{
n++;
如果(勾选!=null)
{
勾选(此,EventArgs.Empty);
}
nextTick+=步长;
}
var interval=GetTimerInterval();
Trace.WriteLine(间隔);
计时器。间隔=间隔;
}
公共无效重置()
{
n=0;
nextTick=0;
}
公开作废开始()
{
var now=DateTime.now.Ticks;
nextTick=now+(步骤-(nextTick%步骤));
timer.Interval=GetTimerInterval();
timer.Start();
}
公共停车场()
{
timer.Stop();
nextTick=DateTime.Now.Ticks%step;
}
}

您可以使用反应式扩展解决这一问题,它将处理大量与计时器相关的问题
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;
    }
}
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();
 int time = 60 - DateTime.Now.Second; // Gets seconds to next minute
        refreshTimer.Interval = time * 1000;
        refreshTimer.Start();
  private void refreshTimer_Tick(object sender, EventArgs e)
    {
        refreshTimer.Interval = 60000; // Sets interval to 60 seconds
        // Insert Refresh logic
    }
using System;
using System.Reactive.Linq;
namespace ConsoleApplicationExample
{
    class Program
    {
        static void Main()
        {
            Observable.Interval(TimeSpan.FromMinutes(1))
            .Subscribe(_ =>
            {                   
                Console.WriteLine(DateTime.Now.ToString());
            });
            Console.WriteLine(DateTime.Now.ToString()); 
            Console.ReadLine();
        }
    }
}
    static void Main(string[] args)
    {
        //constatnt total miliseconds to one minute
        const Int32 minuteMilisecond = 60 * 1000;

        //get actual datetime
        DateTime actualDateTime = DateTime.UtcNow;

        //compenzation to one minute
        Int32 nexTimer = Environment.TickCount + ((59 - actualDateTime.Second) * 1000) + (999 - actualDateTime.Millisecond);

        //random fuction to simulate different delays on thread
        Random rnd = new Random();

        //main loop
        while (true) 
        {
            if (Environment.TickCount > nexTimer)
            {
                nexTimer += minuteMilisecond;

                //execute your code here every minute
                Console.WriteLine($"actual DateTime: {DateTime.Now.ToString("yyyy.MM.dd HH:mm:ss:ffff")}");
            }

            //random sleep between 100 - 200 ms
            Thread.Sleep(rnd.Next(100, 200));
        }
    }