我正在尝试编写一个无限期运行的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...
}
但是,当我尝试将其作为服务的一部分运行,以便在用户注销时它继续处于活动状态时,Service Manager会顽固地拒绝启动它。我得到了可爱的1053错误,“服务没有及时响应启动或控制请求。”
这里有很多相关问题的答案似乎建议不惜一切代价使用计时器而不是线程休眠。如果我做了这样的事情而不是while / sleep组合,我将如何在每次运行时更改定时器间隔?或者这一切都很好,我打算把我的服务设置错误?
提前多多谢谢!
答案 0 :(得分:6)
Windows服务通常必须在30秒内响应控制请求(通常启动/停止,但也会暂停/恢复)。这意味着,如果您在OnStart
中使用主线程,您的服务将返回您引用的错误。
解决问题的方法是在单独的线程上完成工作,您可以按照您描述的方式自由地使用线程。只需在服务'OnStart
中启动此主题,您就可以在30秒的限制内轻松返回。
顺便说一句,您应该考虑停止的服务而不是while(true)
,而不是{30}。如果你有一个正在休眠的线程,那么没有Abort
线程(坏)或者提供一些正确退出线程的机制,服务将无法正常关闭。这正是大多数人采用投票方法的原因;该服务既可以确定其运行时间,也可以确定是否发生了停止请求。只要此轮询频率<30秒,服务将始终正常关闭。
答案 1 :(得分:1)
如果你想使用定时器,很容易做到。我使用System.Timers.Timer并更改其间隔就像mytimer.Inverval = nextTime.Seconds
或类似的一样简单。
我个人在没有AutoReset = false
的情况下运行计时器(因此它不会自动重启计时器)然后每次醒来它都会运行你的“dowork”,然后在你工作的dowork结束时运行如果你希望它在下一次运行,请根据需要设置间隔,然后再次在计时器上调用Start。
当然,在您的服务中,您的start方法只是设置第一个计时器运行然后返回,以便启动很好而且快速。关机时你只需清理你的计时器(停止和处理等),然后再返回。很干净。
答案 2 :(得分:1)
我想你可能正在寻找这样的东西:
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();
}
}
}