我有一个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上找到的东西。
发布于 2015-10-01 17:14:50
Parallel.Foreach有一个可怕的设计缺陷。随着时间的推移,它容易消耗所有可用的线程池资源。它将产生的线程数量实际上是无限的。在没有人理解的启发式驱动下,每秒最多可以得到两个新的。CoreCLR有一个爬山算法,只是不起作用。
呼叫外部服务
也许,您应该找出调用该服务的正确并行度。你需要通过测试不同的数量来找到答案。
然后,您需要将Parallel.Foreach限制为最多只生成多少个线程。您可以使用固定的并发性TaskScheduler来实现这一点。
或者,您可以将其更改为使用异步IO并使用SemaphoreSlim.WaitAsync。这样就不会阻塞线程。解决了池耗尽以及外部服务的重载问题。
https://stackoverflow.com/questions/32893034
复制相似问题