我正在编写一个Windows服务,一旦启动就会每X小时运行一次。它完成的过程相当密集,所以我想使用后台工作程序。我正在使用设置文件来存储运行和上次运行服务之间的小时数。
我不确定这样做的最佳方式 - 也就是说,我希望服务尽可能少地使用资源,当它运行时,它需要在后台工作程序中运行,报告它做了什么,然后回到空闲模式。
我考虑过使用2名背景工作者。第一个worker将是服务的私有局部变量,运行如下:
while (true)
{
//create new background worker and run
Thread.Sleep(Settings.Default.SleepTimeHours * 3600000);
}
使用在循环的每次迭代中创建的子工作程序,并在完成时销毁。为了支持取消,我想我必须在服务中有第二个工作者的本地实例,但如果当前进程没有运行,它将为null。当辅助工作程序完成时,它将发送我的报告,在设置文件中设置上次运行时间,然后处理该工作程序并将引用设置为null。
我想知道是否有更好的方法来做这个或最佳实践。
由于
答案 0 :(得分:33)
我通常使用Timer,然后在进程开始运行时停止它。
这是一篇解释how to do it的文章。
答案 1 :(得分:15)
这不是一个好主意,因为你将线程锁定在整个“SleepTimeHours”期间,你甚至无法在此期间停止服务。
你可以提供这项服务,以便它可以睡觉,例如5秒,然后检查是否是时候恢复工作,如果没有,再睡5秒钟(如果你需要停止服务,这会给你必要的响应)。
或者:您可能最好只编写一个可以使用Windows“计划任务”功能进行安排的控制台应用程序,以便每x小时运行一次。这样,如果您的应用没有做任何事情,您将不会阻止或使用任何系统资源......
马克
答案 2 :(得分:10)
考虑像Quartz.Net这样的作业调度程序。
答案 3 :(得分:7)
更像这样的事情:
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.
}
}
}
答案 4 :(得分:5)
使用基于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();
}
答案 5 :(得分:2)
你真的不想使用后台工作者。当你在前台有一些东西(比如UI)并且你还想要在后台做其他事情时,会使用后台工作程序。在您的情况下,没有前台处理,您只需要每隔一段时间就可以运行一个作业。
此外,不要打扰睡眠业务等。而是使用调度程序框架(例如Quartz.NET)每隔一段时间唤醒它。这样,当您的服务启动时,它将初始化调度程序,并且在调度程序唤醒之前不会执行任何操作。当调度程序唤醒时,它会调用一个对象的方法,在初始化时它会告诉它调用它。
这样,服务在空闲时几乎不消耗任何CPU,并在需要时全力工作。
答案 6 :(得分:2)
Blog.StackOverflow.com有一篇关于使用缓存过期来处理周期性任务的有趣文章:
http://blog.stackoverflow.com/2008/07/easy-background-tasks-in-aspnet/
答案 7 :(得分:2)
有人对超级用户提出了类似的问题。您可以安装一个监视Windows服务的工具。类似Service Hawk之类的东西可以帮助您保持服务的启动,或者允许您安排自动重启(可能在夜间)以保持服务平稳运行。
答案 8 :(得分:1)
在我的公司,我们依靠Windows任务调度程序来启动服务,然后让服务自行关闭。这确保我们的服务始终在正确的时间运行,并且调度系统将允许报告成功/失败等。
答案 9 :(得分:1)
使用计时器执行此操作。
答案 10 :(得分:1)
我在1小时前和同事们进行了同样的讨论!我选择了while(_isPolling)选项,因为我需要同步进行工作。我不希望其他线程接收相同的工作(Timer方法),并且实现额外的锁定似乎是浪费。
答案 11 :(得分:0)
我应用了Timer和工作线程的组合,到目前为止它工作正常。
我将我的计时器设置为每分钟打勾,如果时间与计划时间匹配,我会创建我的工作线程。
我用这种技术做家务:
if (nowTimeVar.ToShortTimeString().CompareTo(houseKeep.ToShortTimeString()) == 0)
{
myHouseKeeper = new clsHouseKeep(archiveFolder, lastday, myCounter, myLogger);
}
答案 12 :(得分:0)
在检查了很多教程后,我开发了每隔60分钟定期运行的Windows服务,它将在后台启动进程并运行Test.exe。
public partial class Scheduler : ServiceBase
{
private Timer timer1 = null;
public Scheduler()
{
InitializeComponent();
}
// Get the Exe path
private string GetPath()
{
Assembly assembly = Assembly.GetExecutingAssembly();
return Path.Combine(Path.GetDirectoryName(assembly.Location), "Test.exe");
}
// you can right your own custom code
// Above vista version the process works as Administrator- "runas" and in old OS it will Start process as per Administartor rights
public void startprocess()
{
// System.Diagnostics.Process.Start(GetPath());
System.Diagnostics.Process process = null;
System.Diagnostics.ProcessStartInfo processStartInfo;
processStartInfo = new System.Diagnostics.ProcessStartInfo();
processStartInfo.FileName = GetPath();
if (System.Environment.OSVersion.Version.Major >= 6) // Windows Vista or higher
{
processStartInfo.Verb = "runas";
}
else
{
// No need to prompt to run as admin
}
processStartInfo.Arguments = "";
process = System.Diagnostics.Process.Start(processStartInfo);
}
// On start method will load when system Start
// timer1_tick Event will call every hour after once start the service automatically
protected override void OnStart(string[] args)
{
//System.Diagnostics.Debugger.Launch();
timer1 = new Timer();
this.timer1.Interval = 3600000; //every 60 min
this.timer1.Elapsed += new System.Timers.ElapsedEventHandler(this.timer1_Tick);
timer1.Enabled = true;
}
// Timer1_tick will Start process every hour
private void timer1_Tick(object sender, ElapsedEventArgs e)
{
//Write code here to do some job depends on your requirement
startprocess();
}
// Method will stop the service
// we have set the method stop service as false. so service will not stop and run every hour
protected override void OnStop()
{
timer1.Enabled = false;
}
}