并行Linq - 使用比处理器更多的线程(用于非CPU绑定任务)

时间:2009-03-04 20:18:46

标签: c# linq multithreading

我正在使用并行linq,我正在尝试使用这样的必要代码同时下载许多网址:

int threads = 10;
Dictionary<string, string> results = urls.AsParallel( threads ).ToDictionary( url => url, url => GetPage( url );

由于下载网页是网络绑定而不是CPU绑定,因此使用比我的处理器/内核数量更多的线程是非常有益的,因为每个线程中的大部分时间都花在等待网络赶上。但是,从我的双核机器上运行上面的thread = 2与thread = 10相同的事实判断,我认为发送到AsParallel的踏板仅限于核心数量。

有没有办法覆盖这种行为?是否有类似的库没有此限制?

(我已经找到了这样的python库,但需要在.Net中运行的东西)

4 个答案:

答案 0 :(得分:12)

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);
                  });
编辑:嗯。我无法通过一些示例代码让ToDictionary()并行化。它适用于Select(url => GetPage(url))但不适用ToDictionary。会搜索一下。

编辑:好的,我仍然无法让ToDictionary并行化,但你可以解决这个问题。这是一个简短但完整的计划:

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;
    }
}

那么,这使用了多少线程?为什么?善良知道。我有2个处理器,所以不是这样 - 我们已经指定了10个线程,所以不是这样。即使我更改GetPage来锤击CPU,它仍然使用5。

如果你只需要将它用于一个特定的任务 - 并且你不介意稍微有点臭的代码 - 说实话,你可能最好自己实现它。

答案 1 :(得分:7)

默认情况下,.Net限制为与端点服务点(IP:端口)的2个并发连接。这就是为什么如果所有网址都是同一个服务器的话你就不会看到差异。

可以使用ServicePointManager.DefaultPersistentConnectionLimit属性控制它。

答案 2 :(得分:1)

我认为这个问题已经有了很好的答案,但我想提出一个重点。将PLINQ用于非CPU限制的任务原则上是错误的设计。并不是说它不起作用 - 它会,但在不必要时使用多个线程会导致麻烦。

不幸的是,在C#中没有好办法解决这个问题。在F#中,您可以使用并行运行的异步工作流,但在执行异步调用时不会阻塞线程(在封面下,它使用BeginOperationEndOperation方法)。您可以在此处找到更多信息:

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

答案 3 :(得分:0)

监控您的网络流量。如果URL来自同一个域,则可能会限制带宽。更多连接可能实际上不会提供任何加速。