Windows 服务的并行编程
Parallel programming for Windows Service
我有一个 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 个入站连接,每个连接都会触发此代码。每个入站连接的 "buyers" 集合可以包含 1-15 个买家。有时,我们的入站连接数会激增至每分钟 100 多个连接,我们的服务器就会停止运行。
CPU 每台服务器(两台负载平衡的 8 核服务器)上的使用率只有 50% 左右,但线程数继续增加(进程中的线程数激增至 350 个)和我们对每个服务器的响应时间入站连接从 3-4 秒减少到 1.5-2 分钟。
我怀疑上面的代码导致了我们的可伸缩性问题。鉴于 Windows 服务(无 UI)的这种使用场景(I/O 操作的并行性),Parallel.ForEach 是最佳方法吗?我在异步编程方面没有很多经验,我期待着利用这个机会来了解更多关于它的信息,我想我会从这里开始获得一些社区建议来补充我在 Google.
Parallel.Foreach
有一个可怕的设计缺陷。随着时间的推移,它很容易耗尽所有可用的线程池资源。它将产生的线程数量实际上是无限的。在无人理解的启发式驱动下,您每秒最多可以获得 2 个新的。 CoreCLR 有一个内置的爬山算法,但它不起作用。
call out to an external service
您可能应该了解调用该服务的正确并行度是多少。你需要通过测试不同的量来找出答案。
然后,您需要限制 Parallel.Foreach
最多只生成您想要的线程数。您可以使用固定并发 TaskScheduler
.
来做到这一点
或者,您将其更改为使用异步 IO 并使用 SemaphoreSlim.WaitAsync
。这样就没有线程被阻塞。池耗尽由此解决,外部服务过载也得到解决。
我有一个 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 个入站连接,每个连接都会触发此代码。每个入站连接的 "buyers" 集合可以包含 1-15 个买家。有时,我们的入站连接数会激增至每分钟 100 多个连接,我们的服务器就会停止运行。
CPU 每台服务器(两台负载平衡的 8 核服务器)上的使用率只有 50% 左右,但线程数继续增加(进程中的线程数激增至 350 个)和我们对每个服务器的响应时间入站连接从 3-4 秒减少到 1.5-2 分钟。
我怀疑上面的代码导致了我们的可伸缩性问题。鉴于 Windows 服务(无 UI)的这种使用场景(I/O 操作的并行性),Parallel.ForEach 是最佳方法吗?我在异步编程方面没有很多经验,我期待着利用这个机会来了解更多关于它的信息,我想我会从这里开始获得一些社区建议来补充我在 Google.
Parallel.Foreach
有一个可怕的设计缺陷。随着时间的推移,它很容易耗尽所有可用的线程池资源。它将产生的线程数量实际上是无限的。在无人理解的启发式驱动下,您每秒最多可以获得 2 个新的。 CoreCLR 有一个内置的爬山算法,但它不起作用。
call out to an external service
您可能应该了解调用该服务的正确并行度是多少。你需要通过测试不同的量来找出答案。
然后,您需要限制 Parallel.Foreach
最多只生成您想要的线程数。您可以使用固定并发 TaskScheduler
.
或者,您将其更改为使用异步 IO 并使用 SemaphoreSlim.WaitAsync
。这样就没有线程被阻塞。池耗尽由此解决,外部服务过载也得到解决。