等待 Windows 服务中的线程停止
我正在开发我的第一个 Windows 服务项目,其中涉及一些基本的线程/并行性。到目前为止,这已经相当激烈,但我正在慢慢开始理解线程(我想我们会看到这一点......)。
我有一个 Windows 服务,其中包含 2 个松散耦合的工作线程。线程 A 将从 FTP 服务器下载文件,解压缩/准备它们并保存到线程 B 将有 FileSystemWatcher 监视的另一个目录。线程 B 将解析文件,对数据执行许多操作,进行一些 http 调用,最后归档文件并将其从工作目录中删除。
我刚刚开始工作,现在面临的问题是如何等待线程A和线程B都返回。我查看了这个问题: Thread.Join 在多个线程上超时 并有了一个想法。
问题是,如果我们等待 x 个线程返回,每个线程都有 1 秒的超时,如果有足够的线程,即使在正常操作下,服务也可能会出现无响应(我在 SCM 抱怨之前读到的超时是 30 秒,对吧?)。此外,如果我们通过在循环线程时跟踪剩余加入时间来解决这个问题,那么在工作线程集合开始时等待线程的时间越长,剩余线程返回的时间就越少 - 所以最终如果有足够的线程,即使所有线程都在预期的时间内返回,服务也会显得无响应。
在这种情况下,我可能会在 14 秒的超时时间内对 A 和 B 进行 thread.join,因为我只有两个工作线程,而 14 秒似乎足以返回两者。
如果我有可变数量的工作线程(假设> 8),是否值得做这样的事情?这能可靠地工作吗?
注意:不要使用以下内容 - 从多个层面来说这都是一个坏主意。查看答案
protected override void OnStop()
{
// the service is stopping - set the event
Worker.ThreadStopEvent.Set();
// stop the threads
Parallel.ForEach(_workerThreads, t =>
{
t.Join(new TimeSpan(0, 0, 28));
});
}
I'm working on my first Windows Service project that involves some basic threading/parallelism. It's been pretty intense so far but I'm slowly beginning to understand threading (I guess we'll see about that...).
I have a Windows Service which will contain 2 loosely coupled worker threads. Thread A will be downloading files from a FTP server, unzipping/prepping them and saving to another directory where Thread B will have a FileSystemWatcher watching. Thread B will parse the files, do a number of things with the data, make some http calls, and finally archive and remove the files from the working directory.
I'm just beginning the work, and the problem I'm facing right now is how to wait for both thread A and thread B to return. I looked at this question: Thread.Join on multiple threads with timeout and had an idea.
The problem is if we're waiting on x threads to return with a x second timeout for each, with enough threads the service could appear non-responsive even under normal operation (I read the timeout before SCM complains is 30 seconds, right?). In addition, if we solve this problem by keeping track of time remaining to join as we loop over threads, the longer we wait on threads at the beginning of the collection of workers, the less time there is for remaining threads to return - so eventually with enough threads the service will appear non-responsive even though all the threads are returning within an expected period of time.
In this case I'll probably just thread.join both A and B on a 14 second timeout because I have only two workers and 14 seconds seems like enough time to return both.
If I had a variable number of worker threads (let's say > 8) would it be worth doing something like this? Would this work reliably?
Note: Don't use the following - it's a bad idea on multiple levels. See answers
protected override void OnStop()
{
// the service is stopping - set the event
Worker.ThreadStopEvent.Set();
// stop the threads
Parallel.ForEach(_workerThreads, t =>
{
t.Join(new TimeSpan(0, 0, 28));
});
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我建议您不要为每个线程使用超时,而是设置事件,然后定期使用较短的超时执行
Join
。一个极端的版本是:该方法的最大问题是您将始终等待 30 秒,即使所有线程在 5 秒内停止也是如此。
您可以使用休眠一秒钟然后进行检查的循环来做得更好。然后,您可以跟踪哪些线程已停止,并在所有线程停止时退出循环。
不过,您可能最好使用 CountdownEvent。每个线程在停止时都会发出信号,主线程会等待,直到所有线程都停止。
Wait
方法允许您指定超时值。等待之后,您可以通过调用Join
并设置一个非常短的超时来确定是否有任何线程仍然挂起。I would suggest that rather than using a timeout for each thread, you instead set your event and then periodically do the
Join
with a shorter timeout. An extreme version would be:The biggest problem with that approach is that you will always wait 30 seconds, even if all the threads stop in five seconds.
You could do better with a loop that sleeps for one second and then does the checks. You could then keep track of which threads have stopped, and exit the loop when all threads have stopped.
You're probably best off, though, using a CountdownEvent. Each thread signals when it's stopped, and the main thread waits until all threads have stopped. The
Wait
method allows you to specify a timeout value. After the wait, you can determine if any threads are still hung by callingJoin
with a very brief timeout.从技术上讲,您不必等到线程完成(加入),而是等到它们报告完成为止。因此,您可以使用在之间共享的 CountdownEvent 实例所有线程并等待它。所有线程都必须在finally块中减少事件:
并且在关闭并等待线程完成时:
Technically you don't have to wait until the threads are done (Join), but until they are reporting completion. So you can use an instance of a CountdownEvent shared between all the threads and wait for it. All threads would have to decrement the event in a finally block:
and the when doing the shutdown and waiting for threads the finish:
您当前的方法可能效果不佳 - 您无法控制 Parallel.ForEach 实际创建的线程数量,并非所有线程连接都可以并行运行 - 这完全取决于这些任务的调度方式在线程池上。最重要的是,它的效率非常低,因为您生成的所有线程基本上都在等待。
如果这些工作线程没有必须在服务关闭时完成的关键任务,我将使它们全部成为后台线程 - 在这种情况下,您根本不必处理这个问题。
Your current approach might not work well - you do not have any control over how many threads
Parallel.ForEach
really creates, not all the thread joins might run in parallel - it all depends on how these tasks are scheduled on the Thread pool. On top of that it's very inefficient since all the threads you spawn are basically just waiting.If these worker threads do not have a critical task that must be finished on service shutdown I would make them all background threads instead - in that case you wouldn't have to deal with this problem at all.