2015-10-01 71 views
3

并行编程我有一个具有类似于下面的代码Windows服务:为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个连接的峰值,并且我们的服务器停止工作。

每台服务器(两台负载均衡的8台核心服务器)的CPU使用率仅为50%左右,但线程数继续增加(每个入站连接的响应时间从3次增加到350次) -4秒到1.5-2分钟。

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

+0

平均而言,在任何特定时刻有多少请求正在执行? –

+3

这看起来不像'Parallel.For'的好用,因为大多数线程都会阻塞,导致线程池产生新的线程。 IO代替使用异步。 –

+0

会降低MaxDegreeOfParallelism帮助'Parallel.ForEach(列表,新ParallelOptions() \t \t \t { \t \t \t \t MaxDegreeOfParallelism = Environment.ProcessorCount, \t \t \t},parallelAction);' – ziddarth

回答

3

Parallel.Foreach有一个可怕的设计缺陷。随着时间的推移,它很容易消耗所有可用的线程池资源。它会产生的线程数量从字面上看是无限的。在没有人理解的启发式推动下,每秒可以获得2个新的。 CoreCLR内置了爬山算法,但无法正常工作。

调出到外部服务

也许,你应该找出什么是并行的调用该服务的权利的程度。你需要通过测试不同的金额来找出答案。

然后,您需要限制Parallel.Foreach,以最大限度地产生尽可能多的线程。你可以使用固定的并发性TaskScheduler来做到这一点。

或者,您将其更改为使用异步IO并使用SemaphoreSlim.WaitAsync。这样没有线程被阻塞。游泳池耗尽由此解决并且外部服务也超载。