我想使用C#在Windows窗体应用程序中每分钟(按时)每分钟做一些事情。我只是想知道什么是最好的方法?
我可以使用计时器并将其间隔设置为60000,但是要使其在分钟内运行,我必须在分钟内精确地启用它,实际上并不可行。
我可以使用计时器并将其间隔设置为1000。然后在其tick事件中,可以根据我设置的变量检查当前分钟的时钟,如果分钟已更改,则运行我的代码。这让我很担心,因为我正在使我的计算机每1秒检查一次,以便每1分钟执行一次工作。当然这很丑吗?
我正在使用Windows窗体和.Net 2.0,所以不想使用.Net 3.5附带的DispatchTimer
这必须是一个相当普遍的问题。你们有更好的方法吗?
基于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();
}
在我的盒子上,此代码在每分钟.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
您可以设置两个计时器。一个初始的短间隔计时器(可能每秒触发一次,但取决于第二个计时器必须在分钟内触发的精度)。
只有在达到主间隔计时器的所需开始时间之前,才触发短间隔计时器。一旦达到初始时间,就可以激活第二个主间隔计时器,并且可以禁用短间隔计时器。
private void refreshTimer_Tick(object sender, EventArgs e)
{
refreshTimer.Interval = 60000; // Sets interval to 60 seconds
// Insert Refresh logic
}
或者,您可以休眠以暂停执行,直到超时为止,该时间应该接近您的期望时间。这只会在睡眠完成时唤醒计算机,这样可以节省CPU时间,并在处理事件之间使CPU掉电。
这具有修改超时的优点,因此它不会漂移。
Quartz.NET
使用设置为每秒钟运行一次的计时器(或毫秒,无论您的精度阈值是多少),然后在且仅当当前时间在该阈值内超过“分钟”时,才对方法进行编码以运行功能。
我用于计划任务的是一个System.Threading.Timer(System.Threading.TimerCallback,object,int,int),其中回调函数设置为我要基于毫秒间隔提供的代码执行的代码为期间值。
关于aquinas的答案和'polling'的结合:(对多种语言的道歉)>>
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();
}
}
}
怎么样:
int startin = 60 - DateTime.Now.Second;
var t = new System.Threading.Timer(o => Console.WriteLine("Hello"),
null, startin * 1000, 60000);
创建一个每1秒钟触发一次的Timer
控件(通常只进行简单的检查就什么也不做)将为应用程序增加微不足道开销。
只需将Timer
或Environment.TickCount
的值与上次存储的时间(前一个“分钟刻度”)进行比较,您应该有一个相当精确的解决方案。这两个时间值的分辨率约为15ms,足以满足您的目的。
但是请注意,Environment.TickCount
控件的时间间隔不保证如此精确,甚至现在在任何地方,因为它运行在Windows消息循环上,并且与UI的响应能力有关。绝对不要依靠它来获得适度的精确计时-尽管它足以触发重复事件,您可以使用更敏感的方法检查时间,例如上面给出的两个之一。
您可以使用响应式扩展来钉上它,这将为您解决许多与计时器相关的问题(时钟更改,应用程序休眠等)。使用Nuget包Rx-Main和类似这样的代码:
DateTime.Now
阅读这里(搜索“ ISchedulerPeriodic简介”,以了解它正在解决的所有问题:DateTime.Now
我用WPF DispatcherTimer编写了此类,但是您可以将调度程序交换为任何支持从睡眠状态唤醒时进行更改的计时器。
该类以固定的时间步长构建,并支持开始/停止/重置,开始/停止/开始的工作类似于恢复操作。在这方面,计时器就像秒表。
一个时钟实现将只创建一个间隔为1秒的类,并监听事件。请注意,尽管这是一个实时时钟,但是如果滴答声事件花费的时间比完成间隔的时间长,您会注意到该时钟将尝试追赶实时信号,这将导致发出滴答声事件。
Timer
[创建方法或将此代码放在希望计时器启动的位置:
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();
然后在您的刻度事件中,将时间间隔设置为60000:
http://blogs.msdn.com/b/rxteam/archive/2012/06/20/reactive-extensions-v2-0-release-candidate-available-now.aspx
运行一些代码以查看分钟是否每秒更改一次,不需要太多的CPU时间,应该可以接受。
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;
}
}
如何?我认为这是执行定时操作的良好框架。
通过使用ReactiveExtensions,如果您有兴趣进行与打印到控制台一样简单的操作,则可以使用以下代码。
int time = 60 - DateTime.Now.Second; // Gets seconds to next minute
refreshTimer.Interval = time * 1000;
refreshTimer.Start();