我如何安排C#Windows服务每天执行一项任务?

时间:2009-02-02 15:21:08

标签: c# windows-services scheduling scheduled-tasks

我有一个用C#(.NET 1.1)编写的服务,并希望它在每晚午夜执行一些清理操作。我必须保留服务中包含的所有代码,那么最简单的方法是什么?使用Thread.Sleep()并检查翻滚时间?

10 个答案:

答案 0 :(得分:86)

我不会使用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();
    }
}

答案 1 :(得分:69)

结帐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);

答案 2 :(得分:21)

每日任务?听起来它应该只是一个计划任务(控制面板) - 这里不需要服务。

答案 3 :(得分:15)

是否必须是实际服务?你能在Windows控制面板中使用内置的预定任务吗?

答案 4 :(得分:3)

我实现这一目标的方法是使用计时器。

运行服务器计时器,让它每隔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();
        }
    }

该方法的优点在于.SignalTime.Minute / Hour check。

在那里有用于测试等的钩子,但这就是你的经过时间计时器看起来像什么才能使它全部工作。

答案 5 :(得分:2)

正如其他人已经写过的那样,计时器是您描述的场景中的最佳选择。

根据您的具体要求,可能不需要每分钟检查当前时间。 如果您不需要在午夜执行完全的操作,但只是在午夜后的一小时内,您只能检查日期是否已更改Martin's approach

如果您希望在午夜执行操作的原因是您希望计算机上的工作负载较低,请更好地注意:同样的假设通常是由其他人做出的,突然间您有100次清理操作在0之间开始: 00和0:01上午

在这种情况下,您应该考虑在不同的时间开始清理。我通常不是在工作时间做这些事情,而是在半小时(凌晨1点30分,我个人喜好)

答案 6 :(得分:1)

我建议您使用计时器,但设置为每45秒检查一次,而不是分钟。否则,您可能会遇到负载过重的情况,错过了对特定分钟的检查,因为在计时器触发的时间和代码运行的时间之间以及检查当前时间之间,您可能错过了目标分钟。

答案 7 :(得分:0)

您也可以在http://visualstudiogallery.msdn.microsoft.com/a4a4f042-ffd3-42f2-a689-290ec13011f8

尝试TaskSchedulerLibrary

实现抽象类AbstractScheduledTask并调用ScheduleUtilityFactory.AddScheduleTaskToBatch静态方法

答案 8 :(得分:0)

对于那些发现上述解决方案不起作用的人,这是因为你的类中可能有一个this,这意味着一个扩展方法,正如错误信息所说,只对非泛型静态有意义类。你的班级不是静态的。这似乎不是作为扩展方法有意义的东西,因为它作用于有问题的实例,因此删除this

答案 9 :(得分:-2)

试试这个:

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
    }
}