c# - Windows 服务 OnStop 等待完成处理

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

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

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

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

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

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

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

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

    谢谢大家!

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

    最佳答案

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

    enter image description here

    关闭此窗口后,我在 SCM 中的服务状态更改为正在停止,并且我注意到该服务继续在任务管理器中运行。 sleep 结束后(将近 6 分钟后),该过程停止。刷新 SCM 窗口显示服务不再运行。

    我从这件事中拿走了几件事。一、OnStop()应该真正尝试及时停止服务,作为与系统保持良好关系的一部分。二、看你如何OnStop()方法是结构化的,您可以强制服务忽略抢占式停止请求,而不是在您这么说时停止。不建议这样做,但您似乎可以这样做。

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

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

    所以我会建议两件事之一。第一种选择是将处理分成可以单独完成的不同块。当每个块完成时,检查服务是否停止。如果是这样,请优雅地退出线程。如果这不能做到,那么我会介绍一些类似于交易的东西来处理。假设您需要与一堆数据库表进行交互,并且一旦开始就中断流程就会出现问题,因为数据库可能处于不良状态。如果数据库系统允许事务,这将变得相对容易。如果没有,则在内存中进行所有处理并在最后一秒提交更改。这样,您只会在提交更改时阻止关闭,而不是在整个持续时间内阻止。就其值(value)而言,我更喜欢使用 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()回调,触发线程关闭:
    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
    }
    

    关于c# - Windows 服务 OnStop 等待完成处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22534330/

    相关文章:

    c# - 如何将组合框的选定项获取到 C# 中的字符串变量

    wcf - 如何从配置中动态传递端点详细信息?

    c# - 如何卸载 dll 以便在 MEF 重组后将其删除?

    C#:如何实例化私有(private)内部类列表?

    java - 展开数学表达式(展开括号)

    c# - 部分类跨越程序集

    c# - 在 Windows Server 2008 R2 和 IIS7.5 中托管 WCF

    c# - 在应用程序中使用WCF类库

    c# - Windows Phone 模拟器未启动

    c# - C#中的蓝牙编程,移动设备和笔记本电脑之间的连接