25

我想每天在特定时间启动一个 Windows 服务来运行一个功能。

我应该考虑用什么方法来实现这个?计时器还是使用线程?

4

7 回答 7

79

(1) 首次启动时,将_timer.Interval 设置为服务启动和调度时间之间的毫秒数。此示例将计划时间设置为上午 7:00,因为 _scheduleTime = DateTime.Today.AddDays(1).AddHours(7);

(2) 在 Timer_Elapsed 上,如果当前间隔不是 24 小时,则将 _timer.Interval 重置为 24 小时(以毫秒为单位)。

System.Timers.Timer _timer;
DateTime _scheduleTime; 

public WinService()
{
    InitializeComponent();
    _timer = new System.Timers.Timer();
    _scheduleTime = DateTime.Today.AddDays(1).AddHours(7); // Schedule to run once a day at 7:00 a.m.
}

protected override void OnStart(string[] args)
{           
    // For first time, set amount of seconds between current time and schedule time
    _timer.Enabled = true;
    _timer.Interval = _scheduleTime.Subtract(DateTime.Now).TotalSeconds * 1000;                                          
    _timer.Elapsed += new System.Timers.ElapsedEventHandler(Timer_Elapsed);
}

protected void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    // 1. Process Schedule Task
    // ----------------------------------
    // Add code to Process your task here
    // ----------------------------------


    // 2. If tick for the first time, reset next run to every 24 hours
    if (_timer.Interval != 24 * 60 * 60 * 1000)
    {
        _timer.Interval = 24 * 60 * 60 * 1000;
    }  
}

编辑:

有时人们希望将服务安排在第0天开始,而不是明天开始,所以他们会更改DateTime.Today.AddDays(0)。如果他们这样做并设置过去的时间,则会导致将间隔设置为负数时出错。

//Test if its a time in the past and protect setting _timer.Interval with a negative number which causes an error.
double tillNextInterval = _scheduleTime.Subtract(DateTime.Now).TotalSeconds * 1000;
if (tillNextInterval < 0) tillNextInterval += new TimeSpan(24, 0, 0).TotalSeconds * 1000;
_timer.Interval = tillNextInterval;
于 2014-01-03T22:16:22.457 回答
8

您确定需要一项每天只运行一次的服务吗?

也许 Windows 任务计划会是更好的解决方案?

于 2013-10-03T05:45:41.613 回答
8

很好的答案(我使用了你的代码),但是这一行有一个问题:

_timer.Interval = _scheduleTime.Subtract(DateTime.Now).TotalSeconds * 1000;

如果 DateTime.now 晚于 scheduleTime,您将变为负数,这将在分配给 timer.Interval 时产生异常。

我用了:

if (DateTime.now > scheduleTime)
    scheduleTime = scheduleTime.AddHours(24);

然后做减法。

于 2015-11-18T18:18:43.043 回答
4

使用 Windows 内置的任务计划程序 ( http://windows.microsoft.com/en-us/windows7/schedule-a-task ) 或 Quartz.net。

除非......您有一项服务正在执行大量其他处理并且需要一直运行,在这种情况下,Timer 可能是合适的。

于 2013-10-03T05:26:57.523 回答
0
private static double scheduledHour = 10;
private static DateTime scheduledTime;

public WinService()
{
     scheduledTime = DateTime.Today.AddHours(scheduledHour);//setting 10 am of today as scheduled time- service start date
}

private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
      DateTime now = DateTime.Now;
      if (scheduledTime < DateTime.Now)
      {
         TimeSpan span = now - DateTime.Now;
         scheduledTime = scheduledTime.AddMilliseconds(span.Milliseconds).AddDays(1);// this will set scheduled time to 10 am of next day while correcting the milliseconds
         //do the scheduled task here
      }  
}
于 2016-01-20T11:20:13.697 回答
0

你可以用一个线程和一个事件来做到这一点;不需要计时器。

using System;
using System.ServiceProcess;
using System.Threading;

partial class Service : ServiceBase
{
    Thread Thread;

    readonly AutoResetEvent StopEvent;

    public Service()
    {
        InitializeComponent();

        StopEvent = new AutoResetEvent(initialState: false);
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            StopEvent.Dispose();

            components?.Dispose();
        }

        base.Dispose(disposing);
    }

    protected override void OnStart(string[] args)
    {
        Thread = new Thread(ThreadStart);

        Thread.Start(TimeSpan.Parse(args[0]));
    }

    protected override void OnStop()
    {
        if (!StopEvent.Set())
            Environment.FailFast("failed setting stop event");

        Thread.Join();
    }

    void ThreadStart(object parameter)
    {
        while (!StopEvent.WaitOne(Timeout(timeOfDay: (TimeSpan)parameter)))
        {
            // do work here...
        }
    }

    static TimeSpan Timeout(TimeSpan timeOfDay)
    {
        var timeout = timeOfDay - DateTime.Now.TimeOfDay;

        if (timeout < TimeSpan.Zero)
            timeout += TimeSpan.FromDays(1);

        return timeout;
    }
}
于 2017-10-26T22:08:34.150 回答
-1

如果是一天一个,为什么不使用任务调度程序?当您想在一分钟内多次运行任务时,Windows 服务很有用。因此,如果您想在特定时间运行程序,最好使用任务调度程序并在一天中的特定时间设置任务调度程序的事件。我用任务调度器做了很多事情,它很完美。您可以在任务调度程序中设置程序的路由并设置运行它的间隔时间。如果您想每天每 5 分钟运行一次程序,您仍然可以使用任务调度程序及其更好的方法。

于 2019-12-12T08:17:30.057 回答