2009-03-04 18 views
9

我使用並行LINQ,我試圖下載同時使用這樣essentily代碼的網址:並行LINQ - 使用更多的線程多於處理器(非CPU密集型任務)

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

由於下載網頁是網絡綁定而不是CPU綁定的,使用比我的處理器/內核數量更多的線程是非常人性化的,因爲每個線程中的大部分時間都花在等待網絡追趕上。然而,從線程= 2運行上面這個事實來看,在我的雙核心機器上與thread = 10具有相同的性能,我認爲發送給AsParallel的腳本限於內核數量。

有沒有什麼方法可以覆蓋這種行爲?是否有類似的圖書館沒有這個限制?

(我發現爲Python這樣的庫,但需要的東西,在淨工作)

回答

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

那麼,這有多少線程使用? 5.爲什麼?善良知道。我有2個處理器,所以不是這樣 - 我們已經指定了10個線程,所以不是這樣。即使我更改GetPage來錘擊CPU,它仍然使用5。

如果您只需要將其用於一項特定任務 - 並且您不介意稍微有點臭的代碼 - 說實話,您可能最好自己實施它。

+0

我得到了相同的症狀。我運行你的分析,並得到只有一個線程..我想性能從1增加到2線程我看到在我的腦海 – 2009-03-04 20:57:23

0

監控您的網絡流量。如果URL來自同一個域,則可能會限制帶寬。更多的連接可能實際上不會提供任何加速。

6

默認情況下,.Net對最終服務點(IP:端口)的併發連接數有2個限制。這就是爲什麼如果所有的網址都在同一個服務器上,你就不會看到差異。

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

1

我認爲這個問題已經有很好的答案,但我想提出一個重要的觀點。對於未受CPU約束的任務使用PLINQ原則上是錯誤的設計。不是說它不會起作用 - 它會的,但是在不需要的時候使用多線程可能會導致麻煩。

不幸的是,在C#中沒有很好的方法來解決這個問題。在F#中,您可以使用並行運行的異步工作流程,但在執行異步調用時不要阻塞線程(在封面下,它使用BeginOperationEndOperation方法)。你可以在這裏找到更多的信息:

同樣的想法可以在一定程度上在C#中使用,但它看起來有點怪異(但它是更有效)。我寫了那篇關於的文章,也有應略高於我原來的想法更加進化庫:

相關問題