85

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

4

10 回答 10

87

我不会使用 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();
    }
}
于 2009-02-02T15:31:04.833 回答
74

查看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);
于 2009-02-02T15:28:15.107 回答
22

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

于 2009-02-02T15:27:29.630 回答
16

它必须是实际的服务吗?您可以只使用 Windows 控制面板中的内置计划任务吗?

于 2009-02-02T15:26:51.457 回答
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();
        }
    }

该方法的核心在于 e.SignalTime.Minute/Hour 检查。

那里有用于测试等的钩子,但这就是你的计时器看起来可以让它全部工作的样子。

于 2009-02-02T15:26:03.737 回答
2

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

根据您的具体要求,可能不需要每分钟检查一次当前时间。如果您不需要在午夜执行该操作,而是在午夜后一小时内执行,您可以采用Martin 的方法,即仅检查日期是否已更改。

如果您希望在午夜执行操作的原因是您希望计算机上的工作负载较低,那么最好小心:其他人经常做出相同的假设,突然间您在 0:00 和 0 之间开始了 100 个清理操作上午01点

在这种情况下,您应该考虑在不同的时间开始清理。我通常不是在时钟时间做这些事情,而是在半小时(1.30 am 是我个人的偏好)

于 2009-02-02T17:02:04.090 回答
1

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

于 2009-02-02T15:36:12.187 回答
0

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

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

于 2014-03-03T02:56:20.340 回答
0

For those that found the above solutions not working, it's because you may have a this inside your class, which implies an extension method which, as the error message says, only makes sense on a non-generic static class. Your class isn't static. This doesn't seem to be something that makes sense as an extension method, since it's acting on the instance in question, so remove the this.

于 2018-04-24T05:53:09.747 回答
-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
    }
}
于 2013-08-09T14:11:41.587 回答