jjzjj

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

coder 2024-05-23 原文

我实际上在 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 的回复:



    关闭此窗口后,我在 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# - Windows 服务 OnStop 等待完成处理的更多相关文章

    1. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

      我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

    2. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

      我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

    3. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

      我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

    4. ruby - 如何指定 Rack 处理程序 - 2

      Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

    5. ruby-on-rails - 启动 Rails 服务器时 ImageMagick 的警告 - 2

      最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru

    6. ruby-on-rails - s3_direct_upload 在生产服务器中不工作 - 2

      在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo

    7. c# - 如何在 ruby​​ 中调用 C# dll? - 2

      如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

    8. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

      这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

    9. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

      我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

    10. ruby-on-rails - 在 Rails 中调试生产服务器 - 2

      您如何在Rails中的实时服务器上进行有效调试,无论是在测试版/生产服务器上?我试过直接在服务器上修改文件,然后重启应用,但是修改好像没有生效,或者需要很长时间(缓存?)我也试过在本地做“脚本/服务器生产”,但是那很慢另一种选择是编码和部署,但效率很低。有人对他们如何有效地做到这一点有任何见解吗? 最佳答案 我会回答你的问题,即使我不同意这种热修补服务器代码的方式:)首先,你真的确定你已经重启了服务器吗?您可以通过跟踪日志文件来检查它。您更改的代码显示的View可能会被缓存。缓存页面位于tmp/cache文件夹下。您可以尝试手动删除

    随机推荐