将服务与计时器同步

发布于 2024-09-25 00:22:05 字数 1567 浏览 5 评论 0 原文

我正在尝试用 c# 编写一个服务,该服务应该从给定日期开始按给定间隔(超时)运行。如果该日期是将来的日期,则服务应等到到达日期时间才启动。

示例:

  • 如果我将超时设置为 21:00:00 起 1 小时,我希望程序每小时运行一次

  • 如果我将超时设置为从 3999.01.01 21:00:00 开始的 1 小时,我希望程序在该日期之前每小时运行一次

我已经通过以下代码实现了这一点,但它有一些问题!

  1. 当我安装服务(使用 installutil)时,由于“Thread.Sleep()”,服务被标记为启动。该服务似乎挂起并正在“安装”直到启动。

  2. “ServiceTimer_Tick()”内的代码可能需要比预期超时更长的时间。如果发生这种情况,如何防止计时器堆栈增加?

我想到的替代方案:

  • 包括第一次使用“timeout.Interval”,然后在后续调用中重置它,但感觉不对。

  • 我还考虑过放弃整个服务理念并将其编译为可执行文件并设置计划任务。

缩短的示例:

public Service()
{
    _timeout = new TimeSpan(0,1,0,0);
    _timer = new System.Timers.Timer();
    _timer.Interval = _timeout.TotalMilliseconds;
    _timer.Elapsed += new ElapsedEventHandler(ServiceTimer_Tick);            
}

private void ServiceTimer_Tick(object sender, System.Timers.ElapsedEventArgs e)
{            
    lock (_obj)  
    { 
        // Stuff that could take a lot of time
    }
}

public static void Main()
{
    Run(new Service());
}

protected override void OnStart(string[] args)
{
    long current = DateTime.Now.Ticks;
    long start = new DateTime(2010,9,15,21,0,0).Ticks;
    long timeout = _timeout.Ticks;
    long sleep;

    if (current > start)
        sleep = timeout - ((current % timeout)) + (start % timeout);
    else
        sleep = start - current;

    Thread.Sleep(new TimeSpan(sleep));

    _timer.AutoReset = true;
    _timer.Enabled = true;
    _timer.Start();
}

I'm trying to write a service in c# that should be run on a given interval (a timeout) from a given date. If the date is in the future the service should wait to start until the date time is reached.

Example:

  • If I set a timeout to be 1 hour from 21:00:00 I want the program to run every hour

  • If I set a timeout to be 1 hour from 3999.01.01 21:00:00 I want the program to until date and from then run each hour

I have sort of achieved that with the following code, but it has some problems!

  1. When I install the service (with installutil) the service is marked as starting because of the 'Thread.Sleep()'. This service appears to be hanging and is "installing" until started.

  2. The code inside 'ServiceTimer_Tick()' might take longer than the expected timeout. How can I prevent the timer stack from increasing if that happens?

Alternatives I've thought of :

  • include using the 'timeout.Interval' first time and then resetting it subsequent calls, but it doesn't feel right.

  • I've also considered ditching the entire service idea and compile it as a executable and set up a scheduled tasks.

Shortened example:

public Service()
{
    _timeout = new TimeSpan(0,1,0,0);
    _timer = new System.Timers.Timer();
    _timer.Interval = _timeout.TotalMilliseconds;
    _timer.Elapsed += new ElapsedEventHandler(ServiceTimer_Tick);            
}

private void ServiceTimer_Tick(object sender, System.Timers.ElapsedEventArgs e)
{            
    lock (_obj)  
    { 
        // Stuff that could take a lot of time
    }
}

public static void Main()
{
    Run(new Service());
}

protected override void OnStart(string[] args)
{
    long current = DateTime.Now.Ticks;
    long start = new DateTime(2010,9,15,21,0,0).Ticks;
    long timeout = _timeout.Ticks;
    long sleep;

    if (current > start)
        sleep = timeout - ((current % timeout)) + (start % timeout);
    else
        sleep = start - current;

    Thread.Sleep(new TimeSpan(sleep));

    _timer.AutoReset = true;
    _timer.Enabled = true;
    _timer.Start();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

怼怹恏 2024-10-02 00:22:05

使用 System.Threading.Timer 可以更轻松地实现这一点。您可以告诉它在第一次滴答之前等待多长时间,然后告诉它之后滴答的频率。

因此,如果您想在开始前等待 2 天,然后每小时执行一次操作,您可以这样写:

Timer MyTimer = new Timer(TimerCallback, null, TimeSpan.FromHours(48), TimeSpan.FromHours(1));

也就是说,如果这是每小时只需运行一次的操作,那么听起来您真正想要的是然后您可以使用 Windows 任务计划程序安排一个可执行文件。

This is easier with a System.Threading.Timer. You can tell it how long to wait before the first tick, and then how often to tick after that.

So, if you wanted to wait 2 days before starting, and then do something once per hour, you'd write:

Timer MyTimer = new Timer(TimerCallback, null, TimeSpan.FromHours(48), TimeSpan.FromHours(1));

That said, if this is something that only has to run once per hour, then it sounds like what you really want is an executable that you then schedule with Windows Task Scheduler.

帅气称霸 2024-10-02 00:22:05

您可以使用System.Threading.Timer。它支持 dueTimeperiod 这正是您所需要的。

You can use a System.Threading.Timer. It supports both a dueTime and a period which is just what you need.

怂人 2024-10-02 00:22:05

您必须将计时器逻辑移动到从 OnStart 例程生成的单独线程中。那么你的逻辑就不能干扰SCM,服务就会正常启动。

编辑:只是为了详细说明 - 对于此任务,我认为计时器工作得不太好,因为您没有考虑时钟校正,这可能导致偏差(或者如果用户手动更改时钟时间,甚至是不正确的)。这就是为什么在我看来,与小间隔的时钟时间进行比较是首选的原因。

该线程的 Run 例程可能如下所示:

    public void run()
    {
        while (processing)
        {
            //initiate action on every full hour
            if (DateTime.Now.Second == 0 && DateTime.Now.Minute == 0)
            {
                //Do something here
                DoSomething();
                //Make sure we sleep long enough that datetime.now.second > 0
                Thread.Sleep(1000);
            }
            Thread.Sleep(100);
        }
    }

you have to move the timer logic to a separate thread that you spawn from your OnStart routine. Then your logic cannot interfere with the SCM and the service will start normally.

Edit: Just to elaborate - for this task I don't think timers work very well, since you are not taking clock corrections into account which could lead to a skew (or even be incorrect if the user manually changes the clock time). That's why comparing to the clock time in small intervals is imo preferred.

The Run routine of that thread could look like this:

    public void run()
    {
        while (processing)
        {
            //initiate action on every full hour
            if (DateTime.Now.Second == 0 && DateTime.Now.Minute == 0)
            {
                //Do something here
                DoSomething();
                //Make sure we sleep long enough that datetime.now.second > 0
                Thread.Sleep(1000);
            }
            Thread.Sleep(100);
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文