如何正确停止多线程.NET Windows服务?

Mar*_*ark 24 .net windows-services process terminate

我有一个用C#编写的Windows服务,它创建了一个卡车负载的线程并建立了许多网络连接(WMI,SNMP,简单的TCP,http).尝试使用服务MSC管理单元停止Windows服务时,停止服务的调用返回相对较快,但该过程继续运行约30秒左右.

主要问题是它可能需要30秒以上才能停止.我能找到什么,我该如何寻找它?

第二个问题是为什么服务msc管理单元(服务控制器)返回,即使进程仍在运行.有没有办法让它只在进程实际被杀死时返回?

这是服务的OnStop方法中的代码

protected override void OnStop()
{
   //doing some tracing
   //......

   //doing some minor single threaded cleanup here
   //......

   base.OnStop();

   //doing some tracing here
}
Run Code Online (Sandbox Code Playgroud)

编辑以响应Thread清理答案

你们中的许多人已经回答我应该跟踪我的所有线程然后清理它们.我不认为这是一种实用的方法.首先,我无法访问一个位置的所有托管线程.该软件非常庞大,包含不同的组件,项目甚至第三方dll都可以创建线程.我无法在一个位置跟踪所有这些,或者有一个所有线程检查的标志(即使我可以让所有线程检查一个标志,许多线程阻塞信号量之类的东西.当它们阻塞时它们可以检查.我将不得不让他们等待超时,然后再检查这个全局标志和等待.

IsBackround标志是一个有趣的事情要检查.但是,我怎么能知道我是否有运行arround的forground线程?我将不得不检查创建线程的代码的每个部分.有没有其他方法,也许是一个可以帮助我找到这个的工具.

但最终,这个过程确实停止了.我似乎只需要等待一些东西.但是,如果我在OnStop方法中等待X ammount时间,那么它将花费大约30秒+ X来停止.无论我尝试做什么,在OnStop返回实际停止的过程之后,过程似乎需要大约30秒(它不总是30秒,它可以变化).

Mat*_*vis 18

一旦OnStop()回调返回,停止服务的调用就会返回.根据你所展示的内容,你的OnStop()方法做得不多,这就解释了为什么它的返回速度如此之快.

有两种方法可以使您的服务退出.

首先,您可以重做该OnStop()方法以通知所有线程关闭并等待它们在退出之前关闭.正如@DSO建议的那样,您可以使用全局bool标志来执行此操作(确保将其标记为volatile).我通常使用ManualResetEvent,但两者都可以.发出线程信号退出.然后加入具有某种超时期限的线程(我通常使用3000毫秒).如果线程仍未退出,则可以调用该Abort()方法退出它们.一般来说,Abort()方法是不受欢迎的,但考虑到你的过程无论如何都要退出,这并不是什么大问题.如果您一直有一个必须中止的线程,您可以重做该线程以更好地响应关闭信号.

其次,将您的线程标记为后台线程(有关详细信息,请参见此处).听起来您正在为线程使用System.Threading.Thread类,默认情况下这是前台线程.这样做可以确保线程不会阻止进程退出.如果您只执行托管代码,这将正常工作.如果你有一个等待非托管代码的线程,我不确定设置IsBackground属性是否仍然会导致线程在关闭时自动退出,即你仍然可以修改线程模型以使这个线程响应你的关机请求.

  • 我接受了这个答案,因为它提到了 IsBackground 线程属性。这是我唯一需要改变的。我不相信创建任何和每个组件都应该使用的全局标志 - 在我看来这太耦合了。但是,如果线程被正确标记为后台线程,则服务会正常停止。 (2认同)

DSO*_*DSO 11

从OnStop返回时,服务控制管理器(SCM)将返回.因此,您需要修复OnStop实现以阻止所有线程完成.

一般方法是让OnStop发出所有线程停止信号,然后等待它们停止.为了避免无限期阻塞,你可以给线程一个时间限制来停止,然后如果它们花费太长时间就中止它们.

这是我过去所做的:

  1. 创建一个名为Stop的全局bool标志,在服务启动时设置为false.
  2. 调用OnStop方法时,将Stop标志设置为true,然后对所有未完成的工作线程执行Thread.Join.
  3. 每个工作线程负责检查Stop标志,并在其为真时完全退出.此检查应经常进行,并且始终在长时间运行之前进行,以避免延迟服务关闭太长时间.
  4. 在OnStop方法中,在Join调用上也有一个超时,为线程提供一个有限的时间来干净地退出...之后你只是中止它.

注意#4中你应该给你的线程留出足够的时间在正常情况下退出.中止应该只发生在线程挂起的异常情况下......在这种情况下,执行中止并不比用户或系统杀死进程(后者如果计算机正在关闭)更糟糕.