简体   繁体   English

Windows服务OnStop等待完成处理

[英]Windows service OnStop wait for finished processing

I actually develop a Windows service in VS 2012 / .NET 4.5. 我实际上在VS 2012 / .NET 4.5中开发了一个Windows服务。

The service is following the scheme of the code snippet below: 该服务遵循以下代码段的方案:

  • Using a timer 使用计时器
  • Executes some desired operation every couple of minutes. 每隔几分钟执行一些所需的操作。
  • The process takes about 10 minutes to complete 该过程大约需要10分钟才能完成
  • I use a single thread in the service 我在服务中使用单个线程

What I am worried about is that if somebody stops the service via the management console, it might be just during the process that the service is doing. 我担心的是,如果有人通过管理控制台停止服务,它可能只是在服务正在进行的过程中。

I have done some reading about stopping Windows service with request stop, but am a bit lost. 我已经做了一些关于停止请求停止的Windows服务的阅读,但有点迷失。 Sometimes WorkerThreads are created, sometimes ManualResetEvents are created, but up to now I couldn't fully grasp the best way forward for my Windows service. 有时创建WorkerThreads,有时会创建ManualResetEvents,但到目前为止,我无法完全掌握Windows服务的最佳前进方法。

I need to wait until the processing is properly finished in the onStop method before stopping the Windows service. 我需要等到处理在onStop方法中正确完成后再停止Windows服务。

What is the best way forward then, also considering the code snippet below? 那么最好的方法是什么,还要考虑下面的代码片段?

Thanks all! 谢谢大家!

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();
        }
    }
}

As a test case, I put a call to System.Threading.Thread.Sleep(500000) in the OnStop() callback of my Windows service. 作为测试用例,我在Windows服务的OnStop()回调中调用了System.Threading.Thread.Sleep(500000) I started the service and then stopped it. 我启动了服务然后停止了它。 I got the window with the progress bar indicating that the Service Control Manager (SCM) was attempting to stop the service. 我得到了带有进度条的窗口,指示服务控制管理器(SCM)正在尝试停止服务。 After about 2 minutes, I got this response from the SCM: 大约2分钟后,我得到了SCM的回复:

在此输入图像描述

After I dismissed this window, the status of my service in the SCM changed to Stopping , and I noticed that the service continued to run in Task Manager. 在我关闭此窗口后,我在SCM中的服务状态变为Stopping ,我注意到该服务继续在任务管理器中运行。 After the sleep elapsed (nearly 6 minutes later), the process stopped. 睡眠过后(将近6分钟后),过程停止。 Refreshing the SCM window showed the service was no longer running. 刷新SCM窗口显示服务不再运行。

I take a couple of things away from this. 我从这里拿走了一些东西。 First, OnStop() should really attempt to stop the service in a timely manner just as part of playing nice with the system. 首先, OnStop()应该真正尝试及时停止服务,就像玩好系统一样。 Second, depending on how your OnStop() method is structured, you could force the service to ignore a preemptive request to stop, instead stopping when you say so. 其次,根据您的OnStop()方法的结构,您可以强制服务忽略抢先请求停止,而是在您这样做时停止。 This is not recommended, but it appears that you could do this. 不建议这样做,但看起来你可以这样做。

As to your particular situation, the thing you have to understand is that the System.Timers.Timer.Elapsed event fires on a ThreadPool thread . 至于您的特定情况,您必须了解的是System.Timers.Timer.Elapsed事件ThreadPool线程上触发 By definition, this is a background thread , which means that it will not keep the application running. 根据定义,这是一个后台线程 ,这意味着它不会使应用程序保持运行。 When the service is told to shut down, the system will stop all background threads and then exit the process. 当告知服务关闭时,系统将停止所有后台线程,然后退出该进程。 So your concern about keeping the processing going until it is finished despite being told by the SCM to shutdown cannot occur the way you've got things structured currently. 因此,尽管被SCM告知要关闭,但是关于保持处理完成直到完成的关注不会发生在你当前结构化的方式上。 To do that, you'd need to create a formal System.Threading.Thread object, set it as a foreground thread, and then use the timer to trigger this thread to execute (as opposed to being done in the Elapsed callback). 为此,您需要创建一个正式的System.Threading.Thread对象,将其设置为前台线程,然后使用计时器触发此线程执行(而不是在Elapsed回调中完成)。

All of that said, I still think you'll want to play nicely with the system, which means timely shutdown of the service when requested to do so. 所有这些都说,我仍然认为你想要与系统很好地配合,这意味着在要求时及时关闭服务。 What happens if, for example, you need to reboot the machine? 例如,如果您需要重启机器会发生什么? I haven't tested it, but if you force your service to continue running until the processing is complete, the system may indeed wait until the process finishes before actually restarting. 我没有对它进行测试,但是如果你强制你的服务继续运行直到处理完成,那么系统可能会等到进程完成之后再实际重启。 That's not what I would want from my service. 这不是我想要的服务。

So I would suggest one of two things. 所以我建议两件事之一。 The first option would be to break the processing into distinct chunks that can be done individually. 第一种选择是将处理分解为可以单独完成的不同块。 As each chunk is finished, check to see if the service is stopping. 每个块完成后,检查服务是否正在停止。 If so, exit the thread gracefully. 如果是这样,请正常退出线程。 If this cannot be done, then I would introduce something akin to transactions to your processing. 如果无法做到这一点,那么我会向您的处理介绍类似于交易的内容。 Let's say that you're needing to interact with a bunch of database tables and interrupting the flow once it's started becomes problematic because the database may be left in a bad state. 假设您需要与一堆数据库表进行交互,并在流程启动时中断流程会出现问题,因为数据库可能处于错误状态。 If the database system allows transactions, this becomes relatively easy. 如果数据库系统允许事务处理,则相对容易。 If not, then do all the processing you can in memory and commit the changes at the last second. 如果没有,那么在内存中执行所有处理并在最后一秒提交更改。 That way, you only block shutting down while the changes are being committed as opposed to blocking for the entire duration. 这样,您只会在提交更改时阻止关闭,而不是在整个持续时间内阻塞。 And for what it's worth, I do prefer using ManualResetEvent for communicating shutdown commands to threads. 值得一提的是,我更喜欢使用ManualResetEventManualResetEvent命令传递给线程。

To avoid rambling any further, I'll cut it off here. 为了避免进一步漫无目的,我会在这里切断它。 HTH. HTH。

EDIT: 编辑:

This is off the cuff, so I won't verify its accuracy. 这是袖口,所以我不会验证它的准确性。 I'll fix any problem you (or others) may find. 我会解决你(或其他人)可能发现的任何问题。

Define two ManualResetEvent objects, one for shutdown notification and one for processing notification, and the Thread object. 定义两个ManualResetEvent对象,一个用于关闭通知,另一个用于处理通知,以及Thread对象。 Change the OnStart() callback to this: 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();
}

Change your Execute() callback to something like this: 将您的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;
        }
    }
}

Create the Process() method like this: 像这样创建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();
    }
}

Finally, in the OnStop() callback, trigger the thread to shutdown: 最后,在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 - thanks for the great code, really helpful. @Matt - 感谢伟大的代码,真的很有帮助。 I found it worked even better if I added another test on _shutdownEvent: 如果我在_shutdownEvent上添加了另一个测试,我发现它工作得更好:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM