Windows服务OnStop等待完成处理

我实际上是在VS 2012 / .NET 4.5中开发Windows服务。

该服务遵循以下代码段的方案:

  • 使用计时器
  • 每隔几分钟执行一些所需的操作。
  • 该过程大约需要10分钟才能完成
  • 我在服务中使用单个线程

我担心的是,如果有人通过管理控制台停止服务,它可能只是在服务正在进行的过程中。

我已经做了一些关于停止请求停止Windows服务的阅读,但有点迷失。 有时创建WorkerThreads,有时会创建ManualResetEvents,但到目前为止,我无法完全掌握Windows服务的最佳前进方法。

我需要等到处理在onStop方法中正确完成后再停止Windows服务。

那么最好的方法是什么,还要考虑下面的代码片段?

谢谢大家!

namespace ImportationCV { public partial class ImportationCV : ServiceBase { private System.Timers.Timer _oTimer; public ImportationCV() { InitializeComponent(); if (!EventLog.SourceExists(DAL.Utilities.Constants.LOG_JOURNAL)) { EventLog.CreateEventSource(DAL.Utilities.Constants.LOG_JOURNAL, DAL.Utilities.Constants.SOURCE_JOURNAL); } EventLog.Source = DAL.Utilities.Constants.SOURCE_JOURNAL; EventLog.Log = DAL.Utilities.Constants.LOG_JOURNAL; } protected override void OnStart(string[] args) { int intDelai = Properties.Settings.Default.WatchDelay * 1000; _oTimer = new System.Timers.Timer(intDelai); _oTimer.Elapsed += new ElapsedEventHandler(this.Execute); _oTimer.Start(); EventLog.WriteEntry(DAL.Utilities.Constants.LOG_JOURNAL, "Service " + DAL.Utilities.Constants.SERVICE_TITLE + " started at " + DateTime.Now.ToString("HH:mm:ss"), EventLogEntryType.Information); } protected override void OnStop() { if (_oTimer != null && _oTimer.Enabled) { _oTimer.Stop(); _oTimer.Dispose(); } EventLog.WriteEntry(DAL.Utilities.Constants.LOG_JOURNAL, "Service " + DAL.Utilities.Constants.SERVICE_TITLE + " stopped at " + DateTime.Now.ToString("HH:mm:ss"), EventLogEntryType.Information); } private void Execute(object source, ElapsedEventArgs e) { _oTimer.Stop(); try { //Process } catch (Exception ex) { EventLog.WriteEntry(DAL.Utilities.Constants.LOG_JOURNAL, (ex.StackTrace + ("\r\n" + ex.Message)), EventLogEntryType.Error); } _oTimer.Start(); } } } 

作为测试用例,我在Windows服务的OnStop()回调中调用了System.Threading.Thread.Sleep(500000) 。 我启动了服务然后停止了它。 我得到了带有进度条的窗口,指示服务控制管理器(SCM)正在尝试停止服务。 大约2分钟后,我得到了SCM的回复:

在此处输入图像描述

在我关闭此窗口后,我在SCM中的服务状态变为Stopping ,我注意到该服务继续在任务管理器中运行。 睡眠过去后(将近6分钟后),该过程停止。 刷新SCM窗口显示服务不再运行。

我从这里拿走了一些东西。 首先, OnStop()应该真正尝试及时停止服务,就像玩好系统一样。 其次,根据OnStop()方法的结构,您可以强制服务忽略抢先请求停止,而是在您这样做时停止。 不建议这样做,但看起来你可以这样做。

至于您的特定情况,您必须了解的是System.Timers.Timer.Elapsed事件在ThreadPool线程上触发 。 根据定义,这是一个后台线程 ,这意味着它不会使应用程序保持运行。 当告知服务关闭时,系统将停止所有后台线程,然后退出该进程。 因此,尽管被SCM告知关闭,但是关于保持处理完成直到完成的关注不会发生在你当前结构化的方式上。 为此,您需要创建一个正式的System.Threading.Thread对象,将其设置为前台线程,然后使用计时器触发此线程执行(而不是在Elapsed回调中完成)。

所有这些都说,我仍然认为你会想要很好地使用系统,这意味着在要求时及时关闭服务。 例如,如果您需要重启机器会发生什么? 我还没有对它进行过测试,但是如果你强制你的服务继续运行直到处理完成,系统可能会等到进程完成后再实际重启。 这不是我想要的服务。

所以我会建议两件事之一。 第一种选择是将处理分解为可以单独完成的不同块。 每个块完成后,检查服务是否正在停止。 如果是这样,请正常退出线程。 如果无法做到这一点,那么我会向您的处理介绍类似于交易的内容。 假设您需要与一堆数据库表进行交互,并在流程启动时中断流程会出现问题,因为数据库可能处于错误状态。 如果数据库系统允许事务处理,则相对容易。 如果没有,那么在内存中执行所有处理并在最后一秒提交更改。 这样,您只会在提交更改时阻止关闭,而不是在整个持续时间内阻塞。 值得一提的是,我更喜欢使用ManualResetEventManualResetEvent命令传递给线程。

为了避免进一步散步,我会在这里切断它。 HTH。

编辑:

这是袖口,所以我不会validation它的准确性。 我会解决你(或其他人)可能发现的任何问题。

定义两个ManualResetEvent对象,一个用于关闭通知,另一个用于处理通知,以及Thread对象。 将OnStart()回调更改为:

 using System.Threading; using Timer = System.Timers.Timer; // both Threading and Timers have a timer class ManualResetEvent _shutdownEvent = new ManualResetEvent(false); ManualResetEvent _processEvent = new ManualResetEvent(false); Thread _thread; Timer _oTimer; protected override void OnStart(string[] args) { // Create the formal, foreground thread. _thread = new Thread(Execute); _thread.IsBackground = false; // set to foreground thread _thread.Start(); // Start the timer. Notice the lambda expression for setting the // process event when the timer elapses. int intDelai = Properties.Settings.Default.WatchDelay * 1000; _oTimer = new Timer(intDelai); _oTimer.AutoReset = false; _oTimer.Elapsed += (sender, e) => _processEvent.Set(); _oTimer.Start(); } 

将您的Execute()回调更改为以下内容:

 private void Execute() { var handles = new WaitHandle[] { _shutdownEvent, _processEvent }; while (true) { switch (WaitHandle.WaitAny(handles)) { case 0: // Shutdown Event return; // end the thread case 1: // Process Event Process(); _processEvent.Reset(); // reset for next time _oTimer.Start(); // trigger timer again break; } } } 

像这样创建Process()方法:

 private void Process() { try { // Do your processing here. If this takes a long time, you might // want to periodically check the shutdown event to see if you need // exit early. } catch (Exception ex) { // Do your logging here... // You *could * also shutdown the thread here, but this will not // stop the service. _shutdownEvent.Set(); } } 

最后,在OnStop()回调中,触发线程关闭:

 protected override void OnStop() { _oTimer.Stop(); // no harm in calling it _oTimer.Dispose(); _shutdownEvent.Set(); // trigger the thread to stop _thread.Join(); // wait for thread to stop } 

@Matt – 感谢伟大的代码,真的很有帮助。 如果我在_shutdownEvent上添加了另一个测试,我发现它工作得更好:

 case 1: // Process Event Process(); if(_shutdownEvent.WaitOne(0)) break; // don't loop again if a shutdown is needed ...