在Windows服务中运行的线程中最终不会执行

任何人都可以解释为什么这个finally块没有被执行? 我已经阅读了关于何时期望最终阻止不被执行的post,但这似乎是另一种情况。 此代码需要TopShelf和log4net。 我正在运行.net 4.5

我想它必须是Windows服务引擎启动未处理的exception,但为什么它在finally块完成之前运行?

using log4net; using log4net.Config; using System; using System.Threading; using Topshelf; namespace ConsoleApplication1 { public class HostMain { static void Main(string[] args) { HostFactory.Run(x => { x.Service(s => { s.ConstructUsing(name => new HostMain()); s.WhenStarted(tc => tc.Start()); s.WhenStopped(tc => tc.Stop()); }); x.RunAsLocalSystem(); x.SetServiceName("TimerTest"); }); } public void Stop() { LogManager.GetLogger("MyLog").Info("stopping"); } public void Start() { XmlConfigurator.Configure(); LogManager.GetLogger("MyLog").Info("starting"); new Thread(StartServiceCode).Start(); } public void StartServiceCode() { try { LogManager.GetLogger("MyLog").Info("throwing"); throw new ApplicationException(); } finally { LogManager.GetLogger("MyLog").Info("finally"); } } } } 

输出

 starting throwing stopping 

编辑:请评论你降级的原因,也许你不明白这个问题? 我在这看到一个大问题。 您编写了一些域逻辑,它在Exception的finally子句中执行重要的操作。 然后,如果您在Windows服务中托管逻辑,则设计突然中断。

来自MDSN try-finally(C#参考)

在处理的exception中保证运行关联的finally块 。 但是,如果未处理exception,则finally块的执行取决于如何触发exception展开操作。 反过来,这取决于您的计算机的设置方式。 有关更多信息,请参阅CLR中的未处理的exception处理 。

通常,当未处理的exception结束应用程序时,无论finally块是否运行都不重要

这是设计,.NET选择终止你的应用程序,原因是,有一些非常错误,有些东西没有按预期工作,通过最后调用,我们不想做更多的损害,所以最好是结束应用程序。

如果最后又抛出一个例外,那会怎么样? 如果应用程序即将关闭,它可能已关闭或开始关闭托管资源,并且访问它们以进行登录最终可能会变得致命。

您是否确保记录器在服务停止时销毁记录器之前有机会刷新到磁盘?

编辑

当服务启动时,它会在新线程上发生。 在Topshelf代码中有一个AppDomain.CurrentDomain.UnhandledException + = CatchUnhandledException; 处理程序。

  void CatchUnhandledException(object sender, UnhandledExceptionEventArgs e) { _log.Fatal("The service threw an unhandled exception", (Exception)e.ExceptionObject); HostLogger.Shutdown(); if (e.IsTerminating) { _exitCode = TopshelfExitCode.UnhandledServiceException; _exit.Set(); #if !NET35 // it isn't likely that a TPL thread should land here, but if it does let's no block it if (Task.CurrentId.HasValue) { return; } #endif // this is evil, but perhaps a good thing to let us clean up properly. int deadThreadId = Interlocked.Increment(ref _deadThread); Thread.CurrentThread.IsBackground = true; Thread.CurrentThread.Name = "Unhandled Exception " + deadThreadId.ToString(); while (true) Thread.Sleep(TimeSpan.FromHours(1)); } } 

这会捕获未处理的exception,并通过设置manualresetevent来停止服务(这是阻止服务结束的唯一因素)。

调用sleep后,将发出线程信号,并终止服务线程上的finally块。

然后代码退出。

这是在ConsoleRunHost中的Run()方法中连接的。

  public TopshelfExitCode Run() { Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); AppDomain.CurrentDomain.UnhandledException += CatchUnhandledException; if (_environment.IsServiceInstalled(_settings.ServiceName)) { if (!_environment.IsServiceStopped(_settings.ServiceName)) { _log.ErrorFormat("The {0} service is running and must be stopped before running via the console", _settings.ServiceName); return TopshelfExitCode.ServiceAlreadyRunning; } } bool started = false; try { _log.Debug("Starting up as a console application"); _log.Debug("Thread.CurrentThread.Name"); _log.Debug(Thread.CurrentThread.Name); _exit = new ManualResetEvent(false); _exitCode = TopshelfExitCode.Ok; Console.Title = _settings.DisplayName; Console.CancelKeyPress += HandleCancelKeyPress; if (!_serviceHandle.Start(this)) throw new TopshelfException("The service failed to start (return false)."); started = true; _log.InfoFormat("The {0} service is now running, press Control+C to exit.", _settings.ServiceName); _exit.WaitOne(); } catch (Exception ex) { _log.Error("An exception occurred", ex); return TopshelfExitCode.AbnormalExit; } finally { if (started) StopService(); _exit.Close(); (_exit as IDisposable).Dispose(); HostLogger.Shutdown(); } return _exitCode; } 

无法保证最终会被调用某些例外。

由于此程序作为Windows服务运行,因此由Windows管理。 Windows检测到由于ApplicationException调用而出现问题,并且它向服务发送Stop ,该服务在执行finally块之前中止线程。

“finally”块永远不会执行,因为Windows从底层拉出地毯 。 当您提醒exception处理如何工作时,这是完全符合逻辑的:

 try { // Do stuff } catch (Exception e) { // Executed first } finally { // Executed last } 

由于您没有提供catch块,因此ApplicationException会传播到其他层,最终传播到Windows服务管理,它通过发送stop请求来处理它,从而中止线程。

附注:

  • 服务中的非托管exception非常糟糕:显然,您应该添加catch块并记录exception。
  • 通常, Stop函数用于告知需要停止的工作线程。 这将使线程有机会以干净的方式停止。 这是一个很好的例子 。

编辑:

这是我要做的一个例子。 它更像伪代码,但你应该得到这个想法。

 public void StartServiceCode(object state) { bool stopTimer = false; try { LogManager.GetLogger("MyLog").Info("Locking"); lock (thisLock) { LogManager.GetLogger("MyLog").Info("Throwing"); throw new ApplicationException(); } } catch (Exception e) { // The lock is relased automatically // Logging the error (best practice) LogManager.GetLogger("MyLog").Info("Exception occurred..."); // If severe, we need to stop the timer if (e is StackOverflowException || e is OutOfMemoryException) stopTimer = true; } finally { // Always clean up LogManager.GetLogger("MyLog").Info("finally"); } // Do we need to stop? if (stopTimer) { LogManager.GetLogger("MyLog").Info("Severe exception : stopping"); // You need to keep a reference to the timer. (yes, a timer can stop itself) timer.Stop(); } } 

很抱歉这是一个答案,但无法发表评论。 我找不到任何关于Windows服务的具体内容,但我假设它使用后台/前台线程来执行代码。

在线程方面,finally块有时无效(如果线程被意外中止或中断) – http://blog.goyello.com/2014/01/21/threading-in-c-7-things-you -should-始终记得,大约/

或者更正式的post – (寻找前景/背景线程部分) https://msdn.microsoft.com/en-us/library/orm-9780596527570-03-19.aspx

希望它对你有所帮助

链接的文章解释了为什么方法的finally块遇到由TopShelf库提供的窗口服务引发未处理的exception,它没有被执行: https ://lowleveldesign.wordpress.com/2012/12/03/try- 最后,topshelf-winsvc /

问题似乎与topshelf库中的一部分代码有关,该代码会hibernate引发exception的线程。

下面是负责线程上睡眠调用的代码的摘录,此方法属于TopShelf库

  ... void CatchUnhandledException(object sender, UnhandledExceptionEventArgs e) { _log.Error("The service threw an unhandled exception", (Exception)e.ExceptionObject); ... int deadThreadId = Interlocked.Increment(ref _deadThread); Thread.CurrentThread.IsBackground = true; Thread.CurrentThread.Name = "Unhandled Exception " + deadThreadId.ToString(); while (true) Thread.Sleep(TimeSpan.FromHours(1)); } ...