如何安排 C# Windows 服务每天执行任务?
Posted
技术标签:
【中文标题】如何安排 C# Windows 服务每天执行任务?【英文标题】:How might I schedule a C# Windows Service to perform a task daily? 【发布时间】:2010-10-04 22:49:44 【问题描述】:我有一个用 C# (.NET 1.1) 编写的服务,并希望它在每晚午夜执行一些清理操作。我必须保留服务中包含的所有代码,那么完成此操作的最简单方法是什么?使用Thread.Sleep()
并检查滚动时间?
【问题讨论】:
您真的想创建一个 .NET 进程,它整天都在内存中,并在午夜做点什么?为什么你不能在安装工具时设置一个在午夜(或其他可配置的时间)触发的系统事件来启动你的应用程序,执行它的操作,然后消失? 我知道如果我发现我有一个托管服务消耗 20-40 兆内存,一直在运行,除了每天一次之外什么都不做,我会有点生气。 :) 其重复的link 【参考方案1】:对于那些发现上述解决方案不起作用的人,这是因为您的类中可能有一个this
,这意味着一个扩展方法,正如错误消息所说,它只对非泛型静态类有意义。您的课程不是静态的。这似乎不是一个有意义的扩展方法,因为它作用于有问题的实例,所以删除this
。
【讨论】:
【参考方案2】:你也可以在这里http://visualstudiogallery.msdn.microsoft.com/a4a4f042-ffd3-42f2-a689-290ec13011f8试试TaskSchedulerLibrary
实现抽象类AbstractScheduledTask
并调用ScheduleUtilityFactory.AddScheduleTaskToBatch
静态方法
【讨论】:
【参考方案3】:我不会使用 Thread.Sleep()。要么使用计划任务(正如其他人提到的那样),要么在你的服务中设置一个计时器,它会定期触发(例如每 10 分钟)并检查自上次运行以来日期是否发生了变化:
private Timer _timer;
private DateTime _lastRun = DateTime.Now.AddDays(-1);
protected override void OnStart(string[] args)
_timer = new Timer(10 * 60 * 1000); // every 10 minutes
_timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
_timer.Start();
//...
private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
// ignore the time, just compare the date
if (_lastRun.Date < DateTime.Now.Date)
// stop the timer while we are running the cleanup task
_timer.Stop();
//
// do cleanup stuff
//
_lastRun = DateTime.Now;
_timer.Start();
【讨论】:
将计时器设置为在午夜到期而不是每分钟醒来查看时间不是更有意义吗? @Kibbee:也许如果服务没有在午夜运行(无论出于何种原因),那么您可能希望在服务再次运行后立即执行该任务。 那么你应该在服务的启动中添加一些代码来确定你是否应该立即运行,因为服务没有运行,它应该运行。您不应该每 10 分钟连续检查一次。检查您是否应该在启动时运行,然后如果不知道下次运行。之后,设置一个计时器,无论您需要多长时间。 @Kibbee:是的,我同意(这是我们正在讨论的一个小细节)。但即使计时器每 10 秒触发一次,它也不会造成任何伤害(即不耗时)。 很好的解决方案,但上面的代码缺少 _timer.start() 并且 _lastRun 应该设置为昨天,如下所示: private DateTime _lastRun = DateTime.Now.AddDays(-1);【参考方案4】:试试这个:
public partial class Service : ServiceBase
private Timer timer;
public Service()
InitializeComponent();
protected override void OnStart(string[] args)
SetTimer();
private void SetTimer()
if (timer == null)
timer = new Timer();
timer.AutoReset = true;
timer.Interval = 60000 * Convert.ToDouble(ConfigurationManager.AppSettings["IntervalMinutes"]);
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Start();
private void timer_Elapsed(object source, System.Timers.ElapsedEventArgs e)
//Do some thing logic here
protected override void OnStop()
// disposed all service objects
【讨论】:
【参考方案5】:查看Quartz.NET。您可以在 Windows 服务中使用它。它允许您根据配置的计划运行作业,它甚至支持简单的“cron 作业”语法。我已经取得了很大的成功。
以下是其用法的简单示例:
// Instantiate the Quartz.NET scheduler
var schedulerFactory = new StdSchedulerFactory();
var scheduler = schedulerFactory.GetScheduler();
// Instantiate the JobDetail object passing in the type of your
// custom job class. Your class merely needs to implement a simple
// interface with a single method called "Execute".
var job = new JobDetail("job1", "group1", typeof(MyJobClass));
// Instantiate a trigger using the basic cron syntax.
// This tells it to run at 1AM every Monday - Friday.
var trigger = new CronTrigger(
"trigger1", "group1", "job1", "group1", "0 0 1 ? * MON-FRI");
// Add the job to the scheduler
scheduler.AddJob(job, true);
scheduler.ScheduleJob(trigger);
【讨论】:
一个好建议 - 只要你做的事情比基本的 Timer 东西稍微复杂一点,你就应该看看 Quartz。 Quartz 非常易于使用,我认为即使是非常简单的任务,也可以继续使用它。它将提供轻松调整日程安排的能力。 超级简单是不是真的?大多数新手都会在这里遇到这个问题***.com/questions/24628372/…【参考方案6】:正如其他人已经写的那样,计时器是您描述的场景中的最佳选择。
根据您的具体要求,可能不需要每分钟检查一次当前时间。 如果您不需要准确在午夜执行该操作,而是在午夜后的一小时内,您可以使用Martin's approach 仅检查日期是否已更改。
如果您希望在午夜执行操作的原因是您预计计算机上的工作负载较低,那么最好小心:其他人经常做出相同的假设,突然间您有 100 个清理操作在 0 之间开始: 00 和 0:01 am
在这种情况下,您应该考虑在不同的时间开始清理。我通常不是在时钟时间,而是在半小时(凌晨 1.30 是我个人的偏好)做这些事情
【讨论】:
【参考方案7】:我建议您使用计时器,但将其设置为每 45 秒检查一次,而不是每分钟检查一次。否则,您可能会遇到负载过重的情况,错过了对特定分钟的检查,因为在计时器触发的时间和您的代码运行并检查当前时间的时间之间,您可能错过了目标分钟。
【讨论】:
如果您不检查以确保它尚未运行一次,那么如果您每 45 秒检查一次,您可能会点击两次 00:00。 好点。这个问题可以通过在检查过程结束时调用 Sleep(15000) 来避免。 (或者可能是 16000 毫秒,只是为了安全起见 ;-) 我同意,您应该检查它是否已经运行,无论您选择什么计时器持续时间。【参考方案8】:每日任务?听起来它应该只是一个计划任务(控制面板) - 这里不需要服务。
【讨论】:
【参考方案9】:它必须是实际的服务吗?可以直接使用windows控制面板内置的定时任务吗?
【讨论】:
由于服务已经存在,在那里添加功能可能更容易。 将诸如此类的狭义服务转换为控制台应用程序应该是微不足道的。 他已经说过,“我必须保留服务中包含的所有代码”。我认为没有必要质疑最初的要求。这取决于,但有时有很好的理由使用服务而不是计划任务。 +1:因为你总是需要从各个方面看待你的问题。 如果我真的每天只运行 一个 任务,那么我想一个简单的控制台应用程序运行一个计划任务就可以了。我的观点是,这一切都取决于具体情况,并且说“不要使用 Windows 服务”不是一个有用的答案 IMO。【参考方案10】:我实现这一点的方法是使用计时器。
运行一个服务器计时器,让它每 60 秒检查一次小时/分钟。
如果是正确的小时/分钟,则运行您的流程。
我实际上已将其抽象为一个我称为 OnceADayRunner 的基类。
让我把代码清理一下,我会在这里发布。
private void OnceADayRunnerTimer_Elapsed(object sender, ElapsedEventArgs e)
using (NDC.Push(GetType().Name))
try
log.DebugFormat("Checking if it's time to process at: 0", e.SignalTime);
log.DebugFormat("IsTestMode: 0", IsTestMode);
if ((e.SignalTime.Minute == MinuteToCheck && e.SignalTime.Hour == HourToCheck) || IsTestMode)
log.InfoFormat("Processing at: Hour = 0 - Minute = 1", e.SignalTime.Hour, e.SignalTime.Minute);
OnceADayTimer.Enabled = false;
OnceADayMethod();
OnceADayTimer.Enabled = true;
IsTestMode = false;
else
log.DebugFormat("Not correct time at: Hour = 0 - Minute = 1", e.SignalTime.Hour, e.SignalTime.Minute);
catch (Exception ex)
OnceADayTimer.Enabled = true;
log.Error(ex.ToString());
OnceADayTimer.Start();
该方法的核心在于 e.SignalTime.Minute/Hour 检查。
那里有用于测试等的钩子,但这是你的计时器看起来可以让它全部工作的样子。
【讨论】:
由于服务器繁忙而导致的轻微延迟可能会导致它错过小时+分钟。 是的。当我这样做时,我检查了:现在时间是否大于 00:00?如果是这样,自我上次运行该工作以来是否已超过 24 小时?如果是,则运行作业,否则再次等待。以上是关于如何安排 C# Windows 服务每天执行任务?的主要内容,如果未能解决你的问题,请参考以下文章
在 C# .NET Web 应用程序中每天安排任务的正确方法
如何在 Windows 命令提示符下每 5 分钟安排一次任务?
如何在 Windows 服务中使用 Quartz.Net 安排任务?