【问题标题】:Parallel programming for Windows ServiceWindows 服务的并行编程
【发布时间】:2015-10-02 01:17:19
【问题描述】:

我有一个 Windows 服务,其代码类似于以下内容:

List<Buyer>() buyers = GetBuyers();
var results = new List<Result();

Parallel.Foreach(buyers, buyer =>
{
    // do some prep work, log some data, etc.

    // call out to an external service that can take up to 15 seconds each to return
    results.Add(Bid(buyer));    
}

// Parallel foreach must have completed by the time this code executes
foreach (var result in results)
{
    // do some work
}

这一切都很好,而且很有效,但我认为我们遇到了可扩展性问题。我们平均每分钟有 20-30 个入站连接,每个连接都会触发此代码。每个入站连接的“买家”集合中可以包含 1-15 个买家。有时,我们的入站连接数会飙升至每分钟 100 多个连接,并且我们的服务器会停止运行。

每台服务器上的 CPU 使用率只有 50% 左右(两台负载平衡的 8 核服务器),但线程数继续增加(进程中的线程数达到 350 个),我们对每个入站连接的响应时间从 3- 4 秒到 1.5-2 分钟。

我怀疑上面的代码是造成我们的可伸缩性问题的原因。鉴于 Windows 服务(无 UI)上的这种使用场景(I/O 操作的并行性),Parallel.ForEach 是最好的方法吗?我在异步编程方面没有太多经验,我期待利用这个机会了解更多关于它的信息,我想我会从这里开始获得一些社区建议,以补充我在 Google 上找到的内容。

【问题讨论】:

  • 平均而言,在任何给定时刻有多少请求被执行?
  • 这看起来不太适合使用Parallel.For,因为大多数线程会阻塞,从而导致线程池产生新线程。对于 IO,请改用异步。
  • 将减少 MaxDegreeOfParallelism 帮助Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount, }, parallelAction);

标签: c# parallel-processing


【解决方案1】:

Parallel.Foreach 有一个可怕的设计缺陷。随着时间的推移,它很容易消耗所有可用的线程池资源。它将产生的线程数实际上是无限的。在无人理解的启发式驱动下,您每秒最多可以获得 2 个新的。 CoreCLR 内置了一个爬山算法,但它不起作用。

调用外部服务

您可能应该找出调用该服务的正确并行度。您需要通过测试不同的数量来找出答案。

然后,您需要将Parallel.Foreach 限制为最多只能生成尽可能多的线程。您可以使用固定并发 TaskScheduler 来做到这一点。

或者,您将其更改为使用异步 IO 并使用 SemaphoreSlim.WaitAsync。这样就不会阻塞任何线程。池耗尽和外部服务的重载也解决了。

【讨论】:

    猜你喜欢
    • 2013-10-30
    • 2010-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多