我有一个包含100,000个用户的列表,我通过传递用户ID来从各种Web源(REST API)获取这些用户的相关数据。
我首先将100,000个用户划分为块,然后并行调用API以获取数据,下面是片段。
ParallelOptions po = new ParallelOptions
{
po.MaxDegreeOfParallelism = -1;
};
Parallel.ForEach(listSubscriberEmail, po, (subscriber) =>
{
ProcessEachSubscriber(subscriber);
});
listSubscriberEmail
是用户列表,方法ProcessEachSubscriber
调用各种API。
在4芯机器上花了大约1个小时。
我将代码库迁移到8核机器,但所用的时间仍然相同。
我想我编写代码的方式基本上应该减少时间,因为理想情况下它现在应该在8个核心上产生8个任务......任何想法为什么会出现这种情况?
答案 0 :(得分:1)
我认为你正朝着正确的方向前进。
首先,我不会设置MaxDegreeOfParallelism
无限制。这可能会使你的线程池匮乏并引发数百个线程做得很少。
一种常见的方法是将parrallelism设置为您的核心数:
ParallelOptions loopOptions = new ParallelOptions
{
MaxDegreeOfParallelism = Environment.ProcessorCount,
};
您可以将这些选项传递给Parallel.For
重载。
其次,您提到从REST API获取用户数据。 据推测,这是一个网络呼叫,因此您需要考虑两个项目:
1)系统默认允许并行使用网络连接数,此默认值很小(我认为是两个或四个)。您可以通过调用:
来覆盖它ServicePointManager.DefaultConnectionLimit = n;
进程中的任何位置(ServicePointManager是那些知道它应该做什么的'环境上下文'接口之一。)
在这种情况下,' n '需要进行一些实验,以了解应用程序的最佳网络带宽以及REST api响应的程度。 (只是为了给你一个想法,我有一个类似的过程,这个设置为16,但我正在调用一个公共REST api,它可能非常强大,并且意味着可以很好地扩展。)
2)利用async
中的新WebClient
方法,以便在等待网络I / O时释放并行线程以执行其他工作。正如 I3arnon 所说,网络I / O不受CPU限制,因此在其上投入额外的内核不会有任何区别。
最后,与任何性能优化一样,添加一些准确测量单个操作时间的日志记录,并查看瓶颈所在。你会经常感到惊讶。在投入大量并行呼叫之前,请关注这些点。