2016-06-11 40 views
2

我有一個ConcurrentQueue和一個我需要獲取源代碼的URL列表。當以ConcurrentQueue對象作爲輸入參數使用Parallel.ForEach時,Pop方法不會工作(應該返回一個字符串)。ConcurrentQueue和Parallel.ForEach

我正在使用MaxDegreeOfParallelism並行設置爲四。我真的需要阻止併發線程的數量。是否使用並行冗餘的隊列?

在此先感謝。

// On the main class 
var items = await engine.FetchPageWithNumberItems(result); 
// Enqueue List of items 
itemQueue.EnqueueList(items); 
var crawl = Task.Run(() => { engine.CrawlItems(itemQueue); }); 

// On the Engine class 
public void CrawlItems(ItemQueue itemQueue) 
{ 
Parallel.ForEach(
      itemQueue, 
      new ParallelOptions {MaxDegreeOfParallelism = 4}, 
      item => 
      { 

       var worker = new Worker(); 
       // Pop doesn't return anything 
       worker.Url = itemQueue.Pop(); 
       /* Some work */ 
      }); 
} 

// Item Queue 
class ItemQueue : ConcurrentQueue<string> 
    { 
     private ConcurrentQueue<string> queue = new ConcurrentQueue<string>(); 

     public string Pop() 
     { 
      string value = String.Empty; 
      if(this.queue.Count == 0) 
       throw new Exception(); 
      this.queue.TryDequeue(out value); 
      return value; 
     } 

     public void Push(string item) 
     { 
      this.queue.Enqueue(item); 
     } 

     public void EnqueueList(List<string> list) 
     { 
      list.ForEach(this.queue.Enqueue); 
     } 
    } 
+1

分享你的進度... –

+1

ItemQueue不應該都來自'ConcurrentQueue'幷包含'ConcurrentQueue',請選擇一個。 –

+0

@Zroq:由於下載URL的源是一個I/O綁定操作,我必須聲明並行性是使用錯誤的工具。異步併發將使用更少的資源並且速度一樣快。 –

回答

0

的問題是與CrawlItems的方法,因爲你不應該在提供給ForEach方法的行動呼籲流行。原因是在每個彈出的項目上都會調用該操作,因此該項目已經彈出。這是該行動有「項目」論點的原因。

我假設你得到null,因爲其他線程已經由ForEach方法彈出了所有項。

因此,你的代碼應該是這樣的:

public void CrawlItems(ItemQueue itemQueue) 
{ 
    Parallel.ForEach(
     itemQueue, 
     new ParallelOptions {MaxDegreeOfParallelism = 4}, 
     item => 
     { 
      worker.Url = item; 
      /* Some work */ 
     }); 
} 
+0

itemQueue.EnqueueList(items);我已經檢查過trully隊列中有41項 – Zroq

+0

如果ItemQueue可以請你分享一下實現嗎? – yonisha

+0

我編輯了答案; – Zroq

2

你不需要ConcurrentQueue<T>如果你打算做的是第一項由一個單獨的線程添加到它,然後遍歷它Parallel.ForEach()。正常的List<T>就足夠了。

而且,你的ItemQueue實現非常可疑:

  • ConcurrentQueue<string>繼承和還包含另一個ConcurrentQueue<string>。這沒有多大意義,令人困惑和低效。

  • ConcurrentQueue<T>上的方法非常小心地設計爲線程安全的。您的Pop()不是線程安全的。可能發生的情況是,您檢查Count,注意它是1,然後撥打TryDequeue()並且沒有得到任何值(即value將爲null),因爲另一個線程在兩次調用之間的時間內將該項從隊列中刪除。