C# 并行Linq-使用比处理器更多的线程(用于非CPU限制的任务)

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

我正在使用并行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=>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
方法)。您可以在此处找到更多信息:

同样的想法在某种程度上也可以在C#中使用,但它看起来有点奇怪(但效率更高)。我写了一篇关于这一点的文章,还有一个库应该比我最初的想法稍微进化一些:


我也有同样的症状。我运行了您的分析,只得到了1个线程。我想我看到的性能从1个线程提高到2个线程是在我的head@DrFredEdison:那么,如果您使用示例中的Select/ToDictionary表单,会发生什么情况呢?我看到的结果与您几乎相同。现在,我在每个测试运行中使用了大约5个线程..T谢谢你把我带到这一步我想这会为我现在需要的工作找到工作的。
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;
    }
}