如何正确停止多线程 .NET Windows 服务?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1528209/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to properly stop a multi-threaded .NET windows service?
提问by Mark
I have a windows service written in C# that creates a truck load of threads and makes many network connections (WMI, SNMP, simple TCP, http). When attempting to stop the windows service using the Services MSC snap-in, the call to stop the service returns relatively quickly but the process continues to run for about 30 seconds or so.
我有一个用 C# 编写的 Windows 服务,它创建了大量线程并建立了许多网络连接(WMI、SNMP、简单的 TCP、http)。尝试使用服务 MSC 管理单元停止 Windows 服务时,停止服务的调用返回的速度相对较快,但该进程会继续运行大约 30 秒左右。
The primary question is what could be the reason that it is taking 30+ seconds to stop. What can I look for and how do I go about looking for it?
主要问题是需要 30 多秒才能停止的原因可能是什么。我可以寻找什么以及如何寻找?
The secondary question is why is the service msc snap-in (service controller) returning even though the process is still running. Is there a way to get it to only return when the process is actually killed?
第二个问题是为什么即使进程仍在运行,服务 msc 管理单元(服务控制器)也会返回。有没有办法让它只在进程实际被杀死时返回?
Here is the code in the OnStop method of the service
这是服务的 OnStop 方法中的代码
protected override void OnStop()
{
//doing some tracing
//......
//doing some minor single threaded cleanup here
//......
base.OnStop();
//doing some tracing here
}
Edit in response to Thread cleanup answers
编辑以响应线程清理答案
Many of you have answered that I should keep track of all my threads and then clean them up. I don't think that is a practical approach. Firstly, i don't have access to all managed threads in one location. The software is pretty big with different components, projects and even 3rd party dlls that could all be creating threads. There is no way I can keep track of all of them in one location or have a flag that all threads check (even if i could have all threads check a flag, many threads are blocking on things like semaphores. When they are blocking they can't check. I will have to make them wait with a timeout, then check this global flag and the wait again).
你们中的许多人都回答说我应该跟踪我所有的线程,然后清理它们。我不认为这是一种实用的方法。首先,我无法在一个位置访问所有托管线程。该软件非常大,包含不同的组件、项目甚至 3rd 方 dll,它们都可以创建线程。我无法在一个位置跟踪所有这些,或者有一个所有线程检查的标志(即使我可以让所有线程检查一个标志,许多线程都阻塞在信号量之类的东西上。当他们阻塞时,他们可以't check。我将不得不让他们等待超时,然后检查这个全局标志并再次等待)。
The IsBackround flag is an interesting thing to check. Again though, how can I find out if I have any forground threads running arround? I will have to check every section of the code that creates a thread. Is there any other way, maybe a tool that can help me find this out.
IsBackround 标志是一个有趣的检查。再说一次,我怎样才能知道是否有任何前台线程在运行?我将不得不检查创建线程的代码的每一部分。有没有其他方法,也许可以帮助我找到这个的工具。
Ultimately though, the process does stop. It would only seem that i need to wait for something. However, if i wait in the OnStop method for X ammount of time, then it takes the process approximately 30 seconds + X to stop. No matter what i try to do, it seems that the process needs approximately 30 seconds (its not always 30 seconds, it can vary) after the OnStop returns for the process to actually stop.
但最终,这个过程确实停止了。看来我只需要等待一些东西。但是,如果我在 OnStop 方法中等待 X 时间,则该过程大约需要 30 秒 + X 才能停止。无论我尝试做什么,似乎在 OnStop 返回后该过程需要大约 30 秒(它并不总是 30 秒,它可能会有所不同)才能真正停止。
采纳答案by Matt Davis
The call to stop the service returns as soon as your OnStop()callback returns. Based on what you've shown, your OnStop()method doesn't do much, which explains why it returns so fast.
一旦您的OnStop()回调返回,停止服务的调用就会返回。根据您所展示的内容,您的OnStop()方法并没有做太多事情,这解释了为什么它返回如此之快。
There are a couple of ways to cause your service to exit.
有几种方法可以使您的服务退出。
First, you can rework the OnStop()method to signal all the threads to close and wait for them to close before exiting. As @DSO suggested, you could use a global bool flag to do this (make sure to mark it as volatile). I generally use a ManualResetEvent, but either would work. Signal the threads to exit. Then join the threads with some kind of timeout period (I usually use 3000 milliseconds). If the threads still haven't exited by then, you can call the Abort()method to exit them. Generally, Abort()method is frowned upon, but given that your process is exiting anyway, it's not a big deal. If you consistently have a thread that has to be aborted, you can rework that thread to be more responsive to your shutdown signal.
首先,您可以修改该OnStop()方法以通知所有线程关闭并在退出之前等待它们关闭。正如@DSO 建议的那样,您可以使用全局 bool 标志来执行此操作(确保将其标记为volatile)。我通常使用 ManualResetEvent,但两者都可以。通知线程退出。然后加入具有某种超时时间的线程(我通常使用 3000 毫秒)。如果到那时线程还没有退出,您可以调用该Abort()方法退出它们。通常,Abort()方法不受欢迎,但考虑到您的进程无论如何都在退出,这没什么大不了的。如果您始终有一个必须中止的线程,您可以重新设计该线程以更好地响应您的关闭信号。
Second, mark your threads as backgroundthreads (see herefor more details). It sounds like you are using the System.Threading.Thread class for threads, which are foreground threads by default. Doing this will make sure that the threads do not hold up the process from exiting. This will work fine if you are executing managed code only. If you have a thread that is waiting on unmanaged code, I'm not sure if setting the IsBackground property will still cause the thread to exit automatically on shutdown, i.e., you may still have rework your threading model to make this thread respond to your shutdown request.
其次,将您的线程标记为后台线程(有关更多详细信息,请参见此处)。听起来您正在将 System.Threading.Thread 类用于线程,默认情况下这些线程是前台线程。这样做将确保线程不会阻止进程退出。如果您仅执行托管代码,这将正常工作。如果您有一个正在等待非托管代码的线程,我不确定设置 IsBackground 属性是否仍会导致线程在关闭时自动退出,即您可能仍然需要重新设计线程模型以使该线程响应您的关闭请求。
回答by DSO
The service control manager (SCM) will return when you return from OnStop. So you need to fix your OnStop implementation to block until all the threads have finished.
当您从 OnStop 返回时,服务控制管理器 (SCM) 将返回。因此,您需要将 OnStop 实现修复为阻塞,直到所有线程都完成。
The general approach is to have OnStop signal all your threads to stop, and then wait for them to stop. To avoid blocking indefinitely you can give the threads a time limit to stop, then abort them if they take too long.
一般方法是让 OnStop 通知所有线程停止,然后等待它们停止。为避免无限期阻塞,您可以给线程一个停止的时间限制,然后在它们花费太长时间时中止它们。
Here is what I've done in the past:
这是我过去所做的:
- Create a global bool flag called Stop, set to false when the service is started.
- When OnStop method is called, set the Stop flag to true then do a Thread.Join on all the outstanding worker threads.
- Each worker thread is responsible for checking the Stop flag, and exit cleanly when it is true. This check should be done frequently, and always before a long running operation, to avoid having it delay the service shutdown for too long.
- In the OnStop method, also have a timeout on the Join calls, to give the threads a limited time to exit cleanly... after which you just abort it.
- 创建一个名为 Stop 的全局 bool 标志,在服务启动时设置为 false。
- 当调用 OnStop 方法时,将 Stop 标志设置为 true 然后在所有未完成的工作线程上执行 Thread.Join。
- 每个工作线程负责检查停止标志,并在为真时干净地退出。此检查应经常进行,并始终在长时间运行的操作之前进行,以避免延迟服务关闭时间过长。
- 在 OnStop 方法中,Join 调用也有一个超时,给线程一个有限的时间来干净地退出......之后你只需中止它。
Note in #4 you should give adequate time for your threads to exit in normal case. Abort should only happen in unusual case where thread is hung... in that case doing an abort is no worse than if the user or system kills the process (the latter if the computer is shutting down).
注意#4 你应该给你的线程足够的时间在正常情况下退出。中止应该只在线程挂起的异常情况下发生……在这种情况下,中止并不比用户或系统杀死进程更糟糕(后者在计算机关闭时)。
回答by lsalamon
The simple way to do this may look like this:
-first crete an global event
执行此操作的简单方法可能如下所示:
-first crete a global event
ManualResetEvent shutdownEvent;
-at service start 创建手动重置事件并将其设置为无信号的初始状态
shutdownEvent = new ManualResetEvent(false);
-at service stop event
- 在服务停止事件
shutdownEvent.Set();
do not forget to wait for the end of the threads不要忘记等待线程结束
do
{
//send message for Service Manager to get more time
//control how long you wait for threads stop
}
while ( not_all_threads_stopped );
-each thread must test from time to time, the event to stop
- 每个线程必须不时测试,事件停止
if ( shutdownEvent.WaitOne(delay, true) ) break;
回答by Chaos
For people who look, like me, for a solution to shorter the closing time, try to set the CloseTimeout of your ServiceHost.
对于像我这样看起来想要缩短关闭时间的解决方案的人,请尝试设置 ServiceHost 的 CloseTimeout。
Now I'm trying to understand why it takes so much time to stop without it and I also think it's threads problem. I did look in Visual Studio, attaching to the service and stopping it : I have some threads launched by my service that are still running.
现在我试图理解为什么没有它需要这么多时间才能停止,我也认为这是线程问题。我确实查看了 Visual Studio,附加到服务并停止它:我的服务启动了一些仍在运行的线程。
Now the question is : Is it really these threads that make my service stop so slowly ? Didn't Microsoft think about it ? Don't you think it can be a port releasing problem or something else ? Because it's a waste of time to handle threads sto and finally don't have a shorter closing time.
现在的问题是:真的是这些线程使我的服务停止如此缓慢吗?微软没有考虑过吗?您不认为这可能是端口释放问题或其他问题吗?因为处理线程sto是浪费时间,最后没有更短的关闭时间。
回答by Lawrence Phillips
Matt Davis is pretty complete.
A few points;
If you have a thread that runs forever (because it has has a near-infinite loop and a catch all) and your service 's job is to run that thread, you probably want it to be a foreground thread.
马特戴维斯非常完整。
几点;如果您有一个永远运行的线程(因为它有一个近乎无限的循环和一个catch all)并且您的 service 的工作是运行该线程,您可能希望它成为一个前台线程。
Also, if any of your tasks are performing a longer operation such as a sproc call and so your Join timeout needs to be a little longer, you can actually asked the SCM for more time to shut down. See: https://msdn.microsoft.com/en-us/library/system.serviceprocess.servicebase.requestadditionaltime(v=vs.110).aspxThis can be useful in avoiding the dreaded "marked for deletion" status. The maximum is set in the registry, so I usually request the max expected time the thread usually shuts down in (and never more than 12s). See: what is the maximum time windows service wait to process stop request and how to request for additional time
此外,如果您的任何任务正在执行更长的操作,例如 sproc 调用,因此您的加入超时需要更长一点,您实际上可以要求 SCM 有更多时间关闭。请参阅:https: //msdn.microsoft.com/en-us/library/system.serviceprocess.servicebase.requestadditionaltime(v=vs.110).aspx 这有助于避免可怕的“标记为删除”状态。最大值是在注册表中设置的,所以我通常要求线程通常关闭的最大预期时间(并且永远不会超过 12 秒)。请参阅:Windows 服务等待处理停止请求的最长时间是多少以及如何请求额外时间
My code looks something like:
我的代码看起来像:
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
}
}
}
回答by rama-jka toti
Signal your threads loop exit, do it clean and do thread Join-s.. look for how long it takes as a measure/stopwatch where the problems are. Avoid abortive shutdown for various reasons..
通知您的线程循环退出,清理并执行线程 Join-s .. 寻找问题所在的度量/秒表需要多长时间。避免因各种原因中止关机。
回答by Blue Toque
To answer the first question (Why would the service continue to run for 30+ seconds): there are many reasons. For instance, when using WCF, stopping a the Host causes the process to stop accepting incoming requests, and it waits to process all current requests before stopping.
回答第一个问题(为什么服务会持续运行 30 秒以上):原因有很多。例如,在使用 WCF 时,停止主机会导致进程停止接受传入请求,并在停止之前等待处理所有当前请求。
The same would hold true for may other types of network operations: the operations would attempt to complete before terminating. This is why most network requests have a built-in timeout value for when the request may have "hung" (server gone down, network problems, etc).
这同样适用于其他类型的网络操作:操作将在终止之前尝试完成。这就是为什么大多数网络请求都有一个内置的超时值,用于请求何时可能“挂起”(服务器宕机、网络问题等)。
Without more information on what exactly it is you are doing there is not way to tell you specifically why it's taking 30 seconds, but it's probably a timeout.
如果没有更多关于您正在做什么的信息,就无法具体告诉您为什么需要 30 秒,但这可能是超时。
To answer the second question (Why is the service controller returning): I'm not sure. I know that the ServiceController class has a WaitForState method that allows you to wait untill the given state is reached. It is possible that the service controller is waiting for a predetermined time (another timeout) and then forcibly terminating your application.
回答第二个问题(为什么服务控制器返回):我不确定。我知道 ServiceController 类有一个 WaitForState 方法,它允许您等待直到达到给定的状态。服务控制器可能正在等待预定时间(另一个超时),然后强行终止您的应用程序。
It is also very possible that the base.OnStop method has been called, and the OnStop method has returned, signalling to the ServiceController that the process has stopped, when in fact there are some threads that have not stopped. you are responsible for termingating these threads.
也很有可能是 base.OnStop 方法已被调用,并且 OnStop 方法已返回,向 ServiceController 发出信号,表明进程已停止,而实际上有一些线程尚未停止。您负责终止这些线程。

