等待 Windows 服务中的线程停止

发布于 2024-10-30 05:52:40 字数 1164 浏览 2 评论 0原文

我正在开发我的第一个 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

红衣飘飘貌似仙 2024-11-06 05:52:40

我建议您不要为每个线程使用超时,而是设置事件,然后定期使用较短的超时执行Join。一个极端的版本是:

Worker.ThreadStopEvent.Set();
Thread.Sleep(30000); // wait 30 seconds
// Now try to Join each thread, with a very short (20 ms, maybe) timeout.

该方法的最大问题是您将始终等待 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:

Worker.ThreadStopEvent.Set();
Thread.Sleep(30000); // wait 30 seconds
// Now try to Join each thread, with a very short (20 ms, maybe) timeout.

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 calling Join with a very brief timeout.

月光色 2024-11-06 05:52:40

从技术上讲,您不必等到线程完成(加入),而是等到它们报告完成为止。因此,您可以使用在之间共享的 CountdownEvent 实例所有线程并等待它。所有线程都必须在finally块中减少事件:

void WorkerThread (object args)
{
  try
  {
    // actual work here
    ... 
  }
  finally
  {
    sharedCountdown.Signal();
  }
}

并且在关闭并等待线程完成时:

// Notify all workers of shutdown
...

// Now wait for all workers to complete, up to 30 seconds
sharedCountdown.Wait(TimeSpan.FromSeconds(30));

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:

void WorkerThread (object args)
{
  try
  {
    // actual work here
    ... 
  }
  finally
  {
    sharedCountdown.Signal();
  }
}

and the when doing the shutdown and waiting for threads the finish:

// Notify all workers of shutdown
...

// Now wait for all workers to complete, up to 30 seconds
sharedCountdown.Wait(TimeSpan.FromSeconds(30));
我ぃ本無心為│何有愛 2024-11-06 05:52:40

您当前的方法可能效果不佳 - 您无法控制 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文