C# 定期运行的 Windows 服务
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1498498/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Windows Service that runs Periodically
提问by Josh
I'm writing a windows service that once started will run every X hours. The process it completes is fairly intensive, so I want to use a background worker. I'm using a Settings file to store both the hours between runs and the last time the service ran.
我正在编写一个 Windows 服务,一旦启动,它将每 X 小时运行一次。它完成的过程相当密集,所以我想使用后台工作人员。我正在使用设置文件来存储运行和上次服务运行之间的时间。
I'm not exactly sure the best way to do this - that is, I want the service to idle using as few resources as possible, and when it runs, it needs to run in the background worker, report what it did, and then go back into idle mode.
我不确定这样做的最佳方法 - 也就是说,我希望服务使用尽可能少的资源空闲,并且当它运行时,它需要在后台工作程序中运行,报告它做了什么,然后回到空闲模式。
I've thought about using 2 background workers. The first worker would be a private local variable for the service that runs something like this:
我考虑过使用 2 个后台工作人员。第一个工作器将是运行如下内容的服务的私有局部变量:
while (true)
{
//create new background worker and run
Thread.Sleep(Settings.Default.SleepTimeHours * 3600000);
}
with a sub worker that is created each iteration of the loop, and destroyed when completed. To support cancellation, I think I would have to have a local instance of the second worker available in the service, but it will be null if the process currently is not running. When the secondary worker completes, it would send out my reporting, set the last run time in the settings file, and then dispose of the worker and set the reference to null.
带有一个子工作者,它在循环的每次迭代中创建,并在完成时销毁。为了支持取消,我想我必须在服务中提供第二个工作程序的本地实例,但如果进程当前未运行,它将为空。当辅助工作器完成时,它会发出我的报告,在设置文件中设置上次运行时间,然后处理工作器并将引用设置为空。
I'm wondering if there is a better way to do this or a best practice.
我想知道是否有更好的方法来做到这一点或最佳实践。
Thanks
谢谢
采纳答案by CitizenBane
I typically use a Timer, then stop it when the process starts to run.
我通常使用计时器,然后在进程开始运行时停止它。
Here's an article that explains how to do it.
这是一篇解释如何做到这一点的文章。
回答by JustLoren
In my company, we rely on windows task scheduler to initiate the service, and then have the service shut itself down. This insures our service is always run at the right time and the scheduling system will allow reporting of success / failure etc.
在我公司,我们依靠 Windows 任务调度程序来启动服务,然后让服务自行关闭。这确保我们的服务始终在正确的时间运行,并且调度系统将允许报告成功/失败等。
回答by RichardOD
Use a timer to do this.
使用计时器来做到这一点。
回答by marc_s
This is not a very good idea, since you lock your thread for the full period of "SleepTimeHours" and you won't be able to even stop the service in the meantime.
这不是一个好主意,因为您在“SleepTimeHours”的整个时间段内锁定了您的线程,并且在此期间您甚至无法停止服务。
You could either make this service so that it would sleep for e.g. 5 seconds and then check whether it's time to get back to work, and if not, sleep another 5 seconds (that would give you the necessary responsiveness, if you need to stop the service).
你可以让这个服务休眠 5 秒,然后检查是否是时候恢复工作了,如果不是,再休眠 5 秒(这会给你必要的响应,如果你需要停止服务)。
OR: you might be better off just writing a console app that can be scheduled using the Windows "scheduled tasks" feature in order to be run every x hours. That way, you won't be blocking or using any system resource if your app isn't doing anything......
或者:您最好编写一个控制台应用程序,该应用程序可以使用 Windows 的“计划任务”功能进行计划,以便每 x 小时运行一次。这样,如果您的应用程序没有执行任何操作,您就不会阻塞或使用任何系统资源......
Marc
马克
回答by Austin Salonen
Consider a job scheduler like Quartz.Net.
考虑一个像 Quartz.Net 这样的作业调度器。
回答by zvolkov
You don't really want to use background worker. Background workers are used when you have something going one at the foreground (such as UI) and you also want something else to be done on the background. In your case there's no foreground processing, all you have is a single job that you need to run every once in a while.
你真的不想使用后台工作人员。当您在前台有一些事情(例如 UI)并且您还希望在后台完成其他事情时,就会使用后台工作者。在您的情况下,没有前台处理,您所拥有的只是一个需要每隔一段时间运行一次的作业。
Also, don't bother with the sleeping business etc. Instead, use a scheduler framework, such as Quartz.NETto wake it up every once in a while. This way when your service starts it will initialize the scheduler and do absolutely nothing until scheduler wakes up. When scheduler wakes up it will call a method of an object that you tell it to call when you initialize it.
另外,不要打扰休眠业务等。相反,使用调度程序框架,例如Quartz.NET每隔一段时间唤醒它。这样,当您的服务启动时,它将初始化调度程序,并且在调度程序唤醒之前绝对不做任何事情。当调度程序唤醒时,它会调用一个对象的方法,你告诉它在初始化时调用它。
This way the service will barely consume any CPU when idle and work full steam when needed.
这样,服务在空闲时几乎不会消耗任何 CPU,并在需要时全速工作。
回答by Ian
How about something more like this:
更像这样的事情怎么样:
public class LongRunningService : ServiceBase
{
System.Threading.Thread processThread;
System.Timers.Timer timer;
private Boolean Cancel;
protected override void OnStart(string[] args)
{
timer = new Timer(Settings.Default.SleepTimeHours * 3600000);
timer.Elapsed += new ElapsedEventHandler(timer_Tick);
timer.Start();
Cancel = false;
}
protected override void OnContinue()
{
timer.Start();
}
protected override void OnPause()
{
timer.Stop();
}
protected override void OnStop()
{
if (processThread.ThreadState == System.Threading.ThreadState.Running)
{
Cancel = true;
// Give thread a chance to stop
processThread.Join(500);
processThread.Abort();
}
}
void timer_Tick(object sender, EventArgs e)
{
processThread = new System.Threading.Thread(new ThreadStart(DoWork));
processThread.Start();
}
private void DoWork()
{
try
{
while (!Cancel)
{
if (Cancel) { return; }
// Do General Work
System.Threading.Thread.BeginCriticalRegion();
{
// Do work that should not be aborted in here.
}
System.Threading.Thread.EndCriticalRegion();
}
}
catch (System.Threading.ThreadAbortException tae)
{
// Clean up correctly to leave program in stable state.
}
}
}
回答by Keltex
Blog.StackOverflow.com has an interesting article on using cache expiration to handle periodic tasks:
Blog.StackOverflow.com 有一篇关于使用缓存过期处理周期性任务的有趣文章:
http://blog.stackoverflow.com/2008/07/easy-background-tasks-in-aspnet/
http://blog.stackoverflow.com/2008/07/easy-background-tasks-in-aspnet/
回答by David Kiff
I had the same discussion with colleagues around 1hour ago! I went with the while(_isPolling) option, because i needed the work to happen syncronously. I didnt want the same work being picked up by other thread (the Timer approach), and implementing extra locking for that seemed like a waste.
大约 1 小时前,我与同事进行了同样的讨论!我选择了 while(_isPolling) 选项,因为我需要同步进行工作。我不希望其他线程(Timer 方法)执行相同的工作,为此实现额外的锁定似乎是一种浪费。
回答by Matt Davis
Use an approach based on the System.Threading.WaitHandle approach.
使用基于 System.Threading.WaitHandle 方法的方法。
using System.Threading;
private Thread _thread;
private ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
private ManualResetEvent _scheduleEvent = new ManualResetEvent(false);
private System.Timers.Timer _scheduleTimer = new System.Timers.Timer();
protected override void OnStart(string[] args)
{
// Configure the timer.
_scheduleTimer.AutoReset = false;
_scheduleTimer.Interval = 120000; // 2 minutes in milliseconds
_scheduleTimer.Elapsed += delegate { _scheduleEvent.Set(); }
// Create the thread using anonymous method.
_thread = new Thread( delegate() {
// Create the WaitHandle array.
WaitHandler[] handles = new WaitHandle[] {
_shutdownEvent,
_scheduleEvent
};
// Start the timer.
_scheduleTimer.Start();
// Wait for one of the events to occur.
while (!_shutdownEvent.WaitOne(0)) {
switch (WaitHandle.WaitAny(handles)) {
case 0: // Shutdown Event
break;
case 1: // Schedule Event
_scheduleTimer.Stop();
_scheduleEvent.Reset();
ThreadPool.QueueUserWorkItem(PerformScheduledWork, null);
break;
default:
_shutdownEvent.Set(); // should never occur
}
}
} );
_thread.IsBackground = true;
_thread.Start();
}
protected override void OnStop()
{
// Signal the thread to shutdown.
_shutdownEvent.Set();
// Give the thread 3 seconds to terminate.
if (!_thread.Join(3000)) {
_thread.Abort(); // not perferred, but the service is closing anyway
}
}
private void PerformScheduledWork(object state)
{
// Perform your work here, but be mindful of the _shutdownEvent in case
// the service is shutting down.
//
// Reschedule the work to be performed.
_scheduleTimer.Start();
}