如何正确停止一个多线程的.NET Windows服务?(How to properly stop a

2019-07-21 04:11发布

我已经写在C#中的窗口服务,创建线程的卡车装载,使很多网络连接(WMI,SNMP,简单TCP,HTTP)。 当试图使用服务停止Windows服务MSC管理单元,呼叫停止服务回报比较快,但过程中继续为大约30秒左右运行。

主要的问题是,这可能是它正在采取超过30秒后停止的原因。 我能看什么?我如何去寻找呢?

次要问题是,为什么是服务MSC管理单元(服务控制器)返回即使进程仍在运行。 有没有办法得到它只有返回时,过程实际上是杀?

这是在服务的调用OnStop方法的代码

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

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

   base.OnStop();

   //doing some tracing here
}

编辑回应主题清理答案

你们中许多人回答说,我应该把我的所有线程的歌曲,然后清除它们。 我不认为这是一个可行的方法。 首先,我没有访问所有被管理的线程在一个位置。 该软件与不同的组件,项目,甚至第三方的DLL都可能是创建线程蛮大的。 没有办法,我可以跟踪所有这些的一个地方,或者有所有线程检查(即使我可以拥有所有线程标志检查标志,许多线程阻塞之类的信号量。当他们被阻止,他们可以“T检查。我必须让他们有超时等待,然后再次检查这个全局标志和等待)。

该IsBackround标志是检查一件有趣的事情。 再次,虽然,我怎么能找到,如果我有以防万一运行任何forground线程? 我会检查创建一个线程的代码的每个部分。 是否有任何其他的方式,也许一个工具,可以帮我找到了这一点。

虽然最终,该过程不会停止。 它只会觉得,我需要等待一些东西。 然而,如果在我的时间X ammount的调用OnStop方法等待,则它需要处理大约30秒+ X停止。 不管我怎么努力去做,似乎进程的过程中真正停止调用OnStop返回后,大约需要30秒(它不是始终为30秒,它可以改变)。

Answer 1:

呼叫尽快你停止服务返回OnStop()回调的回报。 基于你已经证明什么,你OnStop()方法并没有做太多,这可以解释为什么它返回这么快。

有一对夫妇的方式来使你的服务退出。

首先,你可以返工OnStop()方法对信号的所有线程关闭,并等待他们退出之前关闭。 作为@DSO建议,你可以使用一个全局布尔标志,以做到这一点(请务必将其标记为volatile )。 我通常使用的ManualResetEvent,但无论是可行的。 信号线程退出。 然后加入了某种超时时间的线程(我一般用3000毫秒)。 如果线程仍然没有通过,然后退出,你可以调用Abort()方法来退出他们。 一般情况下, Abort()方法是令人难以接受的,但考虑到你的进程退出无论如何,这不是什么大不了的事。 如果你一直有一个必须终止线程,可以返工线程更加适应你的关机信号。

其次,标记您的线程为后台线程(见此处了解详情)。 这听起来像你正在使用的线程,这是默认的前景线程System.Threading.Thread类。 这样做将确保该线程不会从退出撑起过程。 如果你只执行托管代码,这将很好地工作。 如果你有一个是在非托管代码等待一个线程,我不知道,如果设置的IsBackground属性仍然会导致线程在关闭自动退出,也就是说,你可能仍然有返工的线程模型,使这个线程响应你的关闭请求。



Answer 2:

当你从调用OnStop返回服务控制管理器(SCM)将返回。 所以,你需要修复您的调用OnStop实施封锁,直到所有线程完成。

一般的方法是让调用OnStop信号中的所有你的线程停止,然后等待他们停止。 为了避免无限期封锁你可以给线程的时间限制停止,则跳过他们,如果他们需要太长时间。

以下是我在过去所做的那样:

  1. 创建一个名为停止一个全球性的布尔标志,当服务启动设置为false。
  2. 当调用OnStop方法被调用时,将停止标志设置为true,然后做所有的优秀工作者线程的Thread.join。
  3. 每个工作线程负责检查停止标志,并退出干净,当它是真的。 这种检查应该经常做的,始终是一个长期运行的操作之前,以避免其拖延服务关闭时间过长。
  4. 在调用OnStop方法,也对加入会议超时,给线程在有限的时间完全退出...之后就只放弃它。

注意#4你应该给予足够的时间让你的线程在正常情况下退出。 中止在线程被挂起异常的情况下,只应该发生......在这种情况下做一个中止不比如果用户或系统终止进程(如果计算机关闭后)更糟糕。



Answer 3:

最简单的方法来做到这一点可能是这样的:
- 第一克里特岛的全球盛会

  ManualResetEvent的shutdownEvent; 

-at服务开始创建手动重置事件,并将其设置为unsignaled的初始状态

shutdownEvent = new ManualResetEvent(false);

-at服务站事件

  shutdownEvent.Set(); 


do
{
 //send message for Service Manager to get more time
 //control how long you wait for threads stop
}
while ( not_all_threads_stopped );

- 每一个线程必须不时地测试,事件停止

if ( shutdownEvent.WaitOne(delay, true) ) break;


Answer 4:

信号你的线程退出循环,做干净,做线程加入-S ..寻找它采取的一项措施/秒表问题所在有多长。 避免因各种原因流产关机..



Answer 5:

要回答第一个问题(为什么会服务继续为超过30秒运行):有多方面的原因。 例如,当使用WCF,停止主机导致进程停止接受传入的请求,并等待停止前处理目前所有的请求。

同样将保持5月其他类型的网络操作的真实:操作会试图终止之前完成。 这就是为什么大多数的网络请求具有当请求可能有一个内置的超时值“挂起”(服务器下降了,网络问题等)。

如果没有对究竟什么是你正在做的没有办法告诉你具体为什么它需要30秒,但它可能是一个超时的更多信息。

要回答第二个问题(为什么服务控制器返回):我不知道。 我知道ServiceController的类有一个WaitForState方法,可以让你等待,直到达到规定的状态。 可能的是,业务控制器在等待预定时间(超时另一个),然后强制结束应用程序。

这也很可能是base.OnStop方法被调用,并且调用OnStop方法恢复,信令进程已经停止,但实际上有一些线程没有停止的ServiceController。 您负责termingating这些线程。



Answer 6:

谁的人看起来像我一样,用于解决关闭时间短,尝试设置你的ServiceHost的CloseTimeout。

现在,我试图理解为什么需要这么多的时间来阻止没有它,我也认为这是线程问题。 我也期待在Visual Studio中,连接到该服务并停止它:我有我的服务仍在运行的推出一些线程。

现在的问题是:是不是真的这些线程,使我的服务站这么慢? 没想到微软呢? 难道你不认为它可以是一个端口释放问题,还是别的什么? 因为它的时间来处理线程STO浪费,终于不用一个较短的关闭时间。



Answer 7:

马特·戴维斯是相当完整。
有几点; 如果你有一直运行(因为它有一个接近无限的循环和包罗万象的),并为您服务的工作是运行线程的线程,你可能希望它是一个前台线程。

另外,如果你的任何任务都执行一个较长的操作,比如一个存储过程调用等你加入超时需要更长一点,实际上你可以问SCM更多的时间来关闭。 请参阅: https://msdn.microsoft.com/en-us/library/system.serviceprocess.servicebase.requestadditionaltime(v=vs.110).aspx这可避免可怕的“标记为删除”状态的有用。 最大的是在注册表中设置,所以我通常要求最大预期时间线程通常在关闭(和从来没有超过12秒以上)。 请参阅: 什么是最大的时间窗口服务等待处理停止请求,以及如何申请额外的时间

我的代码看起来是这样的:

private Thread _worker;       
private readonly CancellationTokenSource _cts = new CancellationTokenSource(); 

protected override void OnStart(string[] args)
{
    _worker = new Thread(() => ProcessBatch(_cts.Token));
    _worker.Start();             
}

protected override void OnStop()
{            
    RequestAdditionalTime(4000);
    _cts.Cancel();            
    if(_worker != null && _worker.IsAlive)
        if(!_worker.Join(3000))
            _worker.Abort(); 
}

private void ProcessBatch(CancellationToken cancelToken)
{
   while (true)
   {
       try
       {
           if(cancelToken.IsCancellationRequested)
                return;               
           // Do work
           if(cancelToken.IsCancellationRequested)
                return;
           // Do more work
           if(cancelToken.IsCancellationRequested)
                return;
           // Do even more work
       }
       catch(Exception ex)
       {
           // Log it
       }
   }
}


文章来源: How to properly stop a multi-threaded .NET windows service?