C# 并行Linq-使用比处理器更多的线程(用于非CPU限制的任务)
我正在使用并行linq,并且我正在尝试使用如下基本代码同时下载许多URL:C# 并行Linq-使用比处理器更多的线程(用于非CPU限制的任务),c#,linq,multithreading,C#,Linq,Multithreading,我正在使用并行linq,并且我正在尝试使用如下基本代码同时下载许多URL: int threads = 10; Dictionary<string, string> results = urls.AsParallel( threads ).ToDictionary( url => url, url => GetPage( url ); int线程=10; Dictionary results=url.AsParallel(线程).ToDictionary(url=>ur
int threads = 10;
Dictionary<string, string> results = urls.AsParallel( threads ).ToDictionary( url => url, url => GetPage( url );
int线程=10;
Dictionary results=url.AsParallel(线程).ToDictionary(url=>url,url=>GetPage(url);
由于下载网页是受网络限制的,而不是CPU限制的,因此使用比我的处理器/内核数量更多的线程是非常有益的,因为每个线程中的大部分时间都花在等待网络赶上。然而,从以下事实判断,在线程=2的情况下运行上述线程与我的d上的线程=10的性能相同双核机,我认为发送到Asparell的踏板数量是有限的
是否有任何方法可以覆盖此行为?是否有类似的库没有此限制
(我为python找到了这样一个库,但需要在.Net中工作的东西)URL是否指向同一台服务器?如果是这样,可能是您遇到了HTTP连接限制而不是线程限制。有一种简单的方法可以告诉您-将代码更改为:
int threads = 10;
Dictionary<string, string> results = urls.AsParallel(threads)
.ToDictionary(url => url,
url => {
Console.WriteLine("On thread {0}",
Thread.CurrentThread.ManagedThreadId);
return GetPage(url);
});
那么,它使用了多少个线程?5。为什么?天知道。我有2个处理器,所以不是它-我们指定了10个线程,所以不是它。即使我更改GetPage
来敲打CPU,它仍然使用5个线程
如果您只需要将其用于一项特定任务,并且您不介意代码有点臭,那么老实说,您最好自己实现它。监控您的网络流量。如果URL来自同一个域,则可能会限制带宽。更多的连接实际上可能不会提供任何加速。默认情况下,.Net一个终端服务点(IP:port)最多只能有2个并发连接。这就是为什么如果所有URL都指向同一个服务器,您不会看到差异
可以使用属性来控制它。我认为这个问题已经有了很好的答案,但我想强调一点。对于不受CPU限制的任务,使用PLINQ原则上是错误的设计。不是说它不起作用-它会起作用,但在不必要时使用多线程可能会造成麻烦 不幸的是,在C#中没有解决此问题的好方法。在F#中,您可以使用并行运行的异步工作流,但在执行异步调用时不阻塞线程(在封面下,它使用
BeginOperation
和interoperation
方法)。您可以在此处找到更多信息:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Linq;
using System.Linq.Parallel;
public class Test
{
static void Main()
{
var urls = Enumerable.Range(0, 100).Select(i => i.ToString());
int threads = 10;
Dictionary<string, string> results = urls.AsParallel(threads)
.Select(url => new { Url=url, Page=GetPage(url) })
.ToDictionary(x => x.Url, x => x.Page);
}
static string GetPage(string x)
{
Console.WriteLine("On thread {0} getting {1}",
Thread.CurrentThread.ManagedThreadId, x);
Thread.Sleep(2000);
return x;
}
}