3

我正在开发我的第一个 Windows 服务项目,该项目涉及一些基本的线程/并行性。到目前为止它非常激烈,但我正在慢慢开始理解线程(我想我们会看到......)。

我有一个 Windows 服务,它将包含 2 个松散耦合的工作线程。线程 A 将从 FTP 服务器下载文件,解压缩/准备它们并保存到线程 B 将有 FileSystemWatcher 监视的另一个目录。线程 B 将解析文件,对数据做一些事情,进行一些 http 调用,最后归档并从工作目录中删除文件。

我才刚刚开始工作,我现在面临的问题是如何等待线程A和线程B都返回。我看了这个问题: Thread.Join on multiple threads with timeout并有了一个想法。

问题是,如果我们正在等待 x 个线程以每个线程的 x 秒超时返回,那么即使在正常操作下,如果有足够多的线程,服务也可能会出现无响应(我在 SCM 抱怨之前读到超时是 30 秒,对吗?)。此外,如果我们通过在循环线程时跟踪加入的剩余时间来解决这个问题,我们在工作线程集合开始时等待线程的时间越长,剩余线程返回的时间就越少 - 所以最终如果有足够的线程,即使所有线程都在预期的时间内返回,服务也会显得无响应。

在这种情况下,我可能会在 14 秒超时后将 A 和 B 都加入线程,因为我只有两个工人,而 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));
        });
    }
4

3 回答 3

2

我建议您不要为每个线程使用超时,而是设置您的事件,然后定期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等待之后,您可以通过非常短暂的超时调用来确定是否有任何线程仍然挂起。

于 2011-04-05T21:59:43.913 回答
2

从技术上讲,您不必等到线程完成(加入),而是等到它们报告完成。因此,您可以使用在所有线程之间共享的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));
于 2011-04-05T22:12:30.610 回答
1

您当前的方法可能效果不佳-您无法控制Parallel.ForEach真正创建的线程数量,并非所有线程连接都可能并行运行-这完全取决于这些任务在线程池上的调度方式。最重要的是,它的效率非常低,因为您生成的所有线程基本上都在等待。

如果这些工作线程没有必须在服务关闭时完成的关键任务,我会将它们设为所有后台线程 - 在这种情况下,您根本不必处理这个问题。

于 2011-04-05T22:00:43.040 回答