C# 中的可调服务定时器

发布于 2024-12-08 05:49:50 字数 780 浏览 0 评论 0原文

我正在尝试编写一个无限期运行的 Windows 服务。 Linux 的 Windows 窗体和后台程序看起来还不错,但也许我只是在 Windows 服务方面非常无能。与我在这里挖掘的其他一些与睡眠或计时器相关的问题不同,醒来或睡觉的时间可以是有规律的间隔,但并不总是这样。该程序从一些数据文件中读取这些数据文件,这些数据文件可能指示它更改自己的计划,并且这必须在下次唤醒时间生效。作为一个控制台程序,它看起来很简单,并且在那里表现得很完美:

while (true)
{
  // Calculate next time to run.
  DateTime nextRun = NextWakeup();
  TimeSpan nextTime = nextRun - DateTime.Now;
  int sleepMs = (int)nextTime.TotalMilliseconds;
  // Sleep until scheduled time
  System.Threading.Thread.Sleep(sleepMs);
  // Do a code cycle of more stuff here...
}

但是,当我尝试将它作为服务的一部分运行以便它在用户注销时继续处于活动状态时,服务管理器顽固地拒绝启动它。我收到可爱的 1053 错误,“服务没有及时响应启动或控制请求。”

这里相关问题的很多答案似乎都建议不惜一切代价使用计时器而不是线程休眠。如果我做了这样的事情而不是 while/sleep 组合,我将如何在每次运行时更改计时器间隔?或者这一切都很好,但我要错误地设置我的服务?

非常感谢!

I'm trying to write a Windows service that runs indefinitely. Windows forms and background programs for Linux don't seem too bad but maybe I'm just horribly inept at Windows Services. Unlike some other sleep or timer related questions I've dug through here, the time to wake up or sleep can be a regular interval, but isn't always such. The program reads from some data files that may instruct it to change its own schedule and this must take effect as of its next wake up time. It seemed quite easy as a console program and behaved perfectly there:

while (true)
{
  // Calculate next time to run.
  DateTime nextRun = NextWakeup();
  TimeSpan nextTime = nextRun - DateTime.Now;
  int sleepMs = (int)nextTime.TotalMilliseconds;
  // Sleep until scheduled time
  System.Threading.Thread.Sleep(sleepMs);
  // Do a code cycle of more stuff here...
}

However, when I try to run it as part of a service so that it continues to be active while the user is logged out, the Service Manager stubbornly refuses to start it. I get the lovely 1053 error, "The service did not respond to the start or control request in a timely fashion."

A lot of answers to related questions here seem to suggest going with a timer at all costs over thread sleeping. If I did such a thing instead of the while/sleep combination, how would I go about changing the timer interval at each run? Or is this all perfectly fine and I'm going about setting up my service wrong?

Much thanks in advance!

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

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

发布评论

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

评论(3

孤独陪着我 2024-12-15 05:49:51

Windows 服务通常必须在 30 秒内响应控制请求(通常是启动/停止,但也包括暂停/恢复)。这意味着如果您在 OnStart 中休眠主线程,您的服务将返回您引用的错误。

解决问题的方法是在单独的线程上完成工作,您可以按照您描述的方式自由地休眠线程。只需在服务的 OnStart 中启动此线程,您应该能够在 30 秒的限制内轻松返回。

顺便说一句,您应该考虑被停止的服务也必须在 30 秒限制内返回,而不是 while(true)。如果您有一个线程处于睡眠状态,那么如果不中止线程(不好)或提供某种正确退出线程的机制,服务将无法正确关闭。这正是大多数人采用投票方式的原因;该服务既可以确定是否到了运行时间,也可以确定是否发生了停止请求。只要轮询频率小于 30 秒,服务就会始终正常关闭。

Windows services must usually respond to a control request (ususally start/stop but also pause/resume) in 30seconds. This means that if you sleep the main thread in the OnStart your service will return the error you refer to.

The way to resolve your issue is to do your work on a separate thread, where you're free to sleep the thread in the way you describe. Just start this thread in the services' OnStart and you should be able to easily return within the 30 second limit.

As an aside, instead of while(true) you should consider the service being stopped must also return in that 30 second limit. If you have a thread sat sleeping the service will not shut down properly without either Aborting the thread (bad) or providing some mechanism for properly exiting the thread. This is exactly why most people go with the polling approach; the service can both determine whether its time to run, or determine whether a stop request has taken place. As long as this poll freqency is <30s the service will always shut down properly.

叹倦 2024-12-15 05:49:51

如果你想使用计时器,这很容易做到。我会使用 System.Timers.Timer 并更改它的间隔就像 mytimer.Inverval = nextTime.Seconds 或类似的一样简单。

我个人会在没有 AutoReset = false 的情况下运行计时器(因此它不会自动重新启动计时器),然后每次它醒来时都会运行您的“dowork”,然后在 dowork 结束时您计算出下次运行的时间,设置适当的时间间隔,然后再次调用计时器上的“开始”。

当然,在您的服务中,您的启动方法只是设置第一个计时器运行然后返回,以便启动又好又快。关闭时,您只需清理计时器(停止并处理等),然后返回即可。漂亮又干净。

If you want to use timers its quite easy to do. I'd use System.Timers.Timer and changing its interval is as easy as mytimer.Inverval = nextTime.Seconds or similar.

I'd personally run the timer without AutoReset = false (so it doesn't restart the timer automatically) and then every time it wakes up it runs your "dowork" and then at the end of the dowork you work out when you want it to run next, set the interval as appropriate and then call Start on your timer again.

Of course in your service your start method just sets up the first timer run and then returns so that the startup is nice and quick. On shutdown you just clean up your timer (stop and dispose and such like) and then just return. Nice and clean.

§对你不离不弃 2024-12-15 05:49:51

我想你可能正在寻找这样的东西:

static class ConsoleProgram
{
    static void Main()
    {
        ServiceBase[] servicesToRun = new ServiceBase[] { new MyService(config, Logger) };
        ServiceBase.Run(servicesToRun);
    }
}



public partial class MyService : ServiceBase
{
    private bool _stopped = true;

    protected override void OnStart(string[] args)
    {
        StartTimer();
    }

    protected override void OnStop()
    {
        StopTimer();
    }

    public void StartTimer()
    {
        _stopped = false;
        Timer t = new Timer(TimerProc);
        // Calculate your desired interval here.
        t.Change(_config.Interval, new TimeSpan(0, 0, 0, 0, -1));
    }

    public void StopTimer()
    {
        _stopped = true;
    }

    private void TimerProc(object state)
    {
        // The state object is the Timer object.
        Timer t = (Timer) state;
        t.Dispose();

        ThreadPool.QueueUserWorkItem(DoWork);

        if (!_stopped) {
            StartTimer();
        }
    }

}

I think you might be looking for something like this:

static class ConsoleProgram
{
    static void Main()
    {
        ServiceBase[] servicesToRun = new ServiceBase[] { new MyService(config, Logger) };
        ServiceBase.Run(servicesToRun);
    }
}



public partial class MyService : ServiceBase
{
    private bool _stopped = true;

    protected override void OnStart(string[] args)
    {
        StartTimer();
    }

    protected override void OnStop()
    {
        StopTimer();
    }

    public void StartTimer()
    {
        _stopped = false;
        Timer t = new Timer(TimerProc);
        // Calculate your desired interval here.
        t.Change(_config.Interval, new TimeSpan(0, 0, 0, 0, -1));
    }

    public void StopTimer()
    {
        _stopped = true;
    }

    private void TimerProc(object state)
    {
        // The state object is the Timer object.
        Timer t = (Timer) state;
        t.Dispose();

        ThreadPool.QueueUserWorkItem(DoWork);

        if (!_stopped) {
            StartTimer();
        }
    }

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文