Windows服务OnStop等待完成处理

时间:2014-03-20 13:22:52

标签: c# .net wcf windows-services single-threaded

我实际上是在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();
        }
    }
}

2 个答案:

答案 0 :(得分:19)

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

enter image description here

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

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

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

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

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

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

编辑:

这不在袖口,所以我不会验证它的准确性。我会解决你(或其他人)可能遇到的任何问题。

定义两个ManualResetEvent个对象,一个用于关闭通知,另一个用于处理通知,以及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()

答案 1 :(得分:0)

@Matt - 感谢伟大的代码,非常有帮助。 如果我在_shutdownEvent上添加了另一个测试,我发现它的效果更好:

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