Windows服务的onStop等待成品加工 [英] Windows service OnStop wait for finished processing

查看:323
本文介绍了Windows服务的onStop等待成品加工的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我实际开发一个Windows服务在VS 2012 / .NET 4.5。

该服务按照以下code段的方案:

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

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

我已经做了一些阅读与要求停止停止Windows服务,但我有点失落。有时WorkerThreads创建,有时候ManualResetEvents创建,但到现在我也没有完全掌握的最好方式向前我的Windows服务。

我要等到处理中的onStop方法正确地完成停止Windows服务之前。

什么是最好的方式前进的话,还要考虑以下code段?

谢谢大家!

命名空间ImportationCV {     公共部分类ImportationCV:ServiceBase的     {         私人System.Timers.Timer的_oTimer;         公共ImportationCV()         {             的InitializeComponent();             如果(!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;         }         保护覆盖无效的OnStart(字串[] args)         {             INT intDelai = Properties.Settings.Default.WatchDelay * 1000;             _oTimer =新System.Timers.Timer的(intDelai);             _oTimer.Elapsed + =新ElapsedEventHandler(this.Execute);             _oTimer.Start();             EventLog.WriteEntry(DAL.Utilities.Constants.LOG_JOURNAL,服务+ DAL.Utilities.Constants.SERVICE_TITLE +开始在+ DateTime.Now.ToString(HH:MM:SS),EventLogEntryType.Information);         }         保护覆盖无效的onStop()         {             如果(_oTimer = NULL和放大器;!&安培; _oTimer.Enabled)             {                 _oTimer.Stop();                 _oTimer.Dispose();             }             EventLog.WriteEntry(DAL.Utilities.Constants.LOG_JOURNAL,服务+ DAL.Utilities.Constants.SERVICE_TITLE +停在+ DateTime.Now.ToString(HH:MM:SS),EventLogEntryType.Information);         }         私人无效执行(对象源,ElapsedEventArgs E)         {             _oTimer.Stop();             尝试             {                 //处理             }             赶上(例外前)             {                 EventLog.WriteEntry(DAL.Utilities.Constants.LOG_JOURNAL,(ex.StackTrace +(\ r \ N+ ex.Message)),EventLogEntryType.Error);             }             _oTimer.Start();         }     } }

解决方案

作为一个测试案例,我把电话给 System.Threading.Thread.Sleep(500000)我的Windows服务的onStop()回调。我开始该服务,然后停止它。我用进度条表示该服务控制管理器(SCM)试图停止服务的窗口。约2分钟后,我从SCM这样的响应:

在我被解雇这个窗口,我在SCM服务的状态更改为停止的,我注意到该服务继续在任务管理器中运行。后睡眠逝去(近6分钟之后),则处理停止。刷新SCM窗口显示服务已不再运行。

我拿几件事情远离这一点。首先,的onStop()真的应该尝试停止及时的服务,就像玩好与系统的一部分。其次,这取决于你的的onStop()方法是结构化的,你的可以的强制服务忽略preemptive请求停止,而不是当你这么说停止。不建议这样做,但现在看来,你的可以的做到这一点。

至于你的具体情况,你必须明白的一点是, System.Timers.Timer.Elapsed 活动的线程池线程fires >。根据定义,这是一个后台线程的,这意味着它不会让应用程序运行。当服务被告知要关闭时,系统将停止所有后台线程,然后退出程序。因此你对,直到它尽管被告知,单片机与停机时完成保持处理持续经营不能会出现你已经得到的东西构成目前的方式。要做到这一点,你需要创建一个正式的 System.Threading.Thread 对象,将其设置为前台线程,然后用计时器来触发这个线程执行(而不是在已用回调正在做的)。

话虽如此,我还是认为你要与系统,这意味着服务的及时关断时要求这样做很好的发挥。如果,例如,你需要重新启动机器,会发生什么?我没有测试它,但如果你强迫你的服务继续运行,直到处理完成后,系统可能真的等到这个过程之前实际重新启动完成。这不是我想从我的服务。

所以我建议两件事情之一。第一种选择是将打破加工成不同的组块可以分别进行。当每个块完成时,检查该服务是否停止。如果是这样,退出线程正常。如果无法做到,那么我会介绍一些类似交易的处理。比方说,你需要用一堆数据库表的交互,并中断流量,一旦它开始变得有问题,因为数据库可能会处于不良状态。如果数据库系统允许事务,这成为比较容易。如果没有,那么所有的处理,你可以在内存中并提交更改在最后一秒。这样一来,你只能封锁关闭,而改变被提交,而不是阻止整个持续时间。而对于它的价值,我用preFER 的ManualResetEvent 传达shutdown命令的线程。

要避免任何进一步的散漫,我会在这里把它割下来。 HTH。

编辑:

这是即兴的,所以我不会验证其准确性。我会解决任何问题,您(或他人)可以找到。

定义两个的ManualResetEvent 的对象,一个用于关闭通知,并加工为一体的通知,而对象。更改的OnStart()回调到这一点:

 使用的System.Threading;

ManualResetEvent的_shutdownEvent =新的ManualResetEvent(假);
ManualResetEvent的_processEvent =新的ManualResetEvent(假);
螺纹_Thread;

保护覆盖无效的OnStart(字串[] args)
{
    //创建正式的,前台线程。
    _Thread =新主题(执行);
    _thread.IsBackground = FALSE; //设置为前台线程
    _thread.Start();

    //启动定时器。注意拉姆达EX pression的设置
    //处理事件时,计时器到期。
    INT intDelai = Properties.Settings.Default.WatchDelay * 1000;
    _oTimer =新System.Timers.Timer的(intDelai);
    _oTimer.AutoReset = FALSE;
    _oTimer.Elapsed + =(发件人,E)=> _processEvent.Set();
    _oTimer.Start();
}
 

更改执行()回调是这样的:

 私人无效执行()
{
    VAR手柄=新的WaitHandle [] {_shutdownEvent,_processEvent};

    而(!_shutdownEvent.WaitOne(0))
    {
        开关(WaitHandle.WaitAny(句柄))
        {
            情况下0://关闭事件
                打破;
            案例1://处理事件
                处理();
                _processEvent.Reset(); //重新设置下一次
                _oTimer.Start();再次//触发定时器
                打破;
        }
    }
}
 

创建进程()的方法是这样的:

 私人无效处理()
{
    尝试
    {
        //这里做你的处理。如果这需要很长的时间,你可能
        //要定期检查关闭事件,看看是否需要
        //退出早。
    }
    赶上(例外前)
    {
        //这里做你的日志记录...

        // *您可以*也关机线程在这里,但是这不会
        //停止服务。
        _shutdownEvent.Set();
    }
}
 

最后,在的onStop()回调,引发线程关机:

 保护覆盖无效的onStop()
{
    _oTimer.Stop(); //在调用它没有坏处
    _oTimer.Dispose();

    _shutdownEvent.Set(); //触发线程停止
    _thread.Join(); //等待线程停止
}
 

I actually develop a Windows service in VS 2012 / .NET 4.5.

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
  • 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. 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.

I need to wait until the processing is properly finished in the onStop method before stopping the Windows service.

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. 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. After about 2 minutes, I got this response from the 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. After the sleep elapsed (nearly 6 minutes later), the process stopped. Refreshing the SCM window showed the service was no longer running.

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. 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. 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. 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. 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).

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.

To avoid rambling any further, I'll cut it off here. 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. Change the OnStart() callback to this:

using System.Threading;

ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
ManualResetEvent _processEvent  = new ManualResetEvent(false);
Thread _thread;

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 System.Timers.Timer(intDelai);
    _oTimer.AutoReset = false;
    _oTimer.Elapsed += (sender, e) => _processEvent.Set();
    _oTimer.Start();
}

Change your Execute() callback to something like this:

private void Execute()
{
    var handles = new WaitHandle[] { _shutdownEvent, _processEvent };

    while (!_shutdownEvent.WaitOne(0))
    {
        switch (WaitHandle.WaitAny(handles))
        {
            case 0:  // Shutdown Event
                break;
            case 1:  // Process Event
                Process();
                _processEvent.Reset();  // reset for next time
                _oTimer.Start();        // trigger timer again
                break;
        }
    }
}

Create the Process() method like this:

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:

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
}

这篇关于Windows服务的onStop等待成品加工的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆