我通常用的timer都是每隔一段时间去执行,如果想指定时刻定时怎么办?
又或者我想每天3点,5点执行任务,每周1到周5执行任务,周六周日休息,又怎么办?
查询了一些资料,大约有4类方法:
1. 轮询制度
思路是仍然设定timer,比如1分钟,每次启动定时任务后,再去判断有没有到指定时间。
static void Main(string[] args)
{
System.Timers.Timer timer = new System.Timers.Timer();
timer.Enabled = true;
timer.Interval = 60000; //执行间隔时间,单位为毫秒; 这里实际间隔为1分钟
timer.Start();
timer.Elapsed += new System.Timers.ElapsedEventHandler(test);
//Console.Readkey()保持Console窗口不关闭,否则,该程序执行后一闪就关闭,不会等1分钟的时间。
Console.ReadKey();
}
private static void test(object source, ElapsedEventArgs e)
{
//指定的每天10:30分执行事件。这里需要注意的是,由于是指定到特定分钟执行事件
//因此,timer.Inverval的时间间隔最长不得超过1分钟,否则,长于1分钟的时间间隔有可能会错过10:30分这个时间节点,从而导致无法触发该事件
if (DateTime.Now.Hour == 10 && DateTime.Now.Minute == 30) //如果当前时间是10点30分
Console.WriteLine("OK, event fired at: " + DateTime.Now.ToString());
}
轮询制度是最简单的方式,缺点稍微消耗资源,不够智能。
2. window系统的计划任务或服务
这种方式把定时任务从代码中解放出来,而是托管给计划任务。
好处是可以随意设定,无需修改代码。
缺点是,这种程序自己用还行,如果分发给用户,难道还要每个用户都去设定一次计划任务?
一旦多个计划任务同时进行,还需要花费额外的精力去管理计划。
用户体验太差了。
从用户体验角度来说,最好定时任务能内嵌在程序内,用户只需要直接双击运行即可,其他无需操心。
3. 对第一种轮询的改进
//设定定时执行
setTaskAtFixedTime();
private void setTaskAtFixedTime()
{
DateTime now = DateTime.Now;
DateTime oneOClock = DateTime.Today.AddHours(1.0); //凌晨1:00
if (now > oneOClock)
{
oneOClock = oneOClock.AddDays(1.0);
}
int msUntilFour = (int)((oneOClock - now).TotalMilliseconds);
var t = new System.Threading.Timer(doAt1AM);
t.Change(msUntilFour, Timeout.Infinite);
}
//要执行的任务
private void doAt1AM(object state)
{
//执行功能...
//再次设定
setTaskAtFixedTime();
}
4. 直接使用第三方编写好的库
这种库不少,比如CronNET,它一个简单的C#库,基于cron调度运行任务。
cron调度最初来源于linux系统,5个*号分别代表—分钟到月—,相当灵活强大。


using System.Threading;
using CronNET;
namespace CronNETExample.Console
{
class Program
{
private static readonly CronDaemon cron_daemon = new CronDaemon();
static void Main(string[] args)
{
cron_daemon.add_job(new CronJob("* * * * *", task));
cron_daemon.start();
// Wait and sleep forever. Let the cron daemon run.
while(true) Thread.Sleep(6000);
}
static void task()
{
Console.WriteLine("Hello, world.")
}
}
}
这里有两个坑,如果是在nuget里下载,会发现以上代码无法通过编译,因为作者不一样。


nuget上的作者是josh mackey,我反复查找也没找到这个包的使用方法。
既没有github,微软nuget官网也没有。
懒得去研究这种三无产品,尽管它的下载量不低,用的人也不少。
以上代码的作者是kevincolyar,项目在github上。

可惜这位仁兄不发nuget,以至于2015年的时候有用户吐槽:
请发到nuget上啊!!!!

热心人士3年后回复:把源码拷贝进自己的项目。
果然硬核,哈哈。
第二个坑在于我下载了源码,并且编译成了dll,仍然发现代码不能正常编译。
查询发现作者修改了类的方法,以前是典型的linux命名法。
现在改成了NET命名法,也没有在示范里说明(2013年估计作者已经不维护了)。
需要修改这两处:
cron_daemon.AddJob("* * * * *", run);
cron_daemon.Start();
完美搞定,正常运行!
这种方法最大的缺点在于,精确控制是分钟级别,如果想控制到秒级别,可以用它和传统的timer配合。
本文介绍了四种在C#中实现每日指定时间定时任务的方法,包括轮询制度、Windows计划任务或服务、轮询改进方案以及使用第三方库CronNET。文章详细讨论了每种方法的优缺点,如轮询消耗资源、计划任务管理复杂以及CronNET的使用陷阱。最后提出了结合CronNET和传统timer来实现秒级精度的解决方案。

1万+

被折叠的 条评论
为什么被折叠?



