0

我想知道替代做toProcess.RemoveAll,但並行。今天我的例子代碼運行良好,但順序,我想平行。列表<T>。刪除所有並行

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace ParallelTest 
{ 
    using System.Threading; 
    using System.Threading.Tasks; 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      List<VerifySomethingFromInternet> foo = new List<VerifySomethingFromInternet>(); 
      foo.Add(new VerifySomethingFromInternet(@"id1", true)); 
      foo.Add(new VerifySomethingFromInternet(@"id2", false)); 
      foo.Add(new VerifySomethingFromInternet(@"id3", true)); 
      foo.Add(new VerifySomethingFromInternet(@"id4", false)); 
      foo.Add(new VerifySomethingFromInternet(@"id5", true)); 
      foo.Add(new VerifySomethingFromInternet(@"id6", false)); 

      DoSomethingFromIntert bar = new DoSomethingFromIntert(); 

      bar.DoesWork(foo); 
      Console.ReadLine(); 
     } 
    } 

    public class DoSomethingFromIntert 
    { 
     bool RemoveIFTrueFromInternet(VerifySomethingFromInternet vsfi) 
     { 
      Console.WriteLine(String.Format("Identification : {0} - Thread : {1}", vsfi.Identification, Thread.CurrentThread.ManagedThreadId)); 
      // Do some blocking work at internet 
      return vsfi.IsRemovable; 
     } 

     public void DoesWork(List<VerifySomethingFromInternet> toProcess) 
     { 
      Console.WriteLine(String.Format("total : {0}", toProcess.Count)); 
      //Remove all true return 
      toProcess.RemoveAll(f => this.RemoveIFTrueFromInternet(f)); 
      Console.WriteLine(String.Format("total : {0}", toProcess.Count)); 
     } 
    } 
    public class VerifySomethingFromInternet 
    { 
     public VerifySomethingFromInternet(string id, bool remove) 
     { 
      this.Identification = id; 
      this.IsRemovable = remove; 
     } 
     public string Identification { get; set; } 
     public bool IsRemovable { get; set; } 
    } 
} 
+1

爲什麼?你確定這是最耗時的部分嗎?似乎不太可能。 –

+0

帕特里克剛剛回答。也許'BlockingCollection'是你在這裏選擇的。它是一種生產者消費類型的集合 – pushpraj

+0

您可以使用並行LINQ獲取可移動項目的列表,從中構建一個哈希集合,最後使用list.removeall和hashset.contains進行實際刪除 – CodesInChaos

回答

5
var newList = toProcess.AsParallel() 
       .Where (f => !this.RemoveIFTrueFromInternet(f)) 
       .ToList(); 

toProcess = newList; 

可能這會回答你的問題,但我不確定它真的更快。嘗試和衡量。

請注意,這可能會更改列表中元素的順序。如果您關心訂單,請在AsParallel之後加AsOrdered。 (感謝weston的[隱含]提示)。

+0

對不起,約翰,我以爲你在我回答後已經到達了這個地方,因此答案出現在頂端。 – weston

+0

刪除了我的,什麼也沒加。感謝Enumerable.Range提示,我喜歡。 – weston

+0

感謝John對你的代碼,非常簡單和優雅 –

3

List<T>不是線程安全的,因此沒有辦法與此類列表並行地執行此操作。

您可以使用線程安全的ConcurrentBag來代替,但該方法顯然沒有RemoveAll方法。

您也可以將列表轉換爲數組,編輯該列表並將其傳遞給列表。

+0

感謝您的建議,我不知道。 –

0

我試圖調整你的代碼有點

我用BlockingCollection實現生產者消費者方案

這不是平行消除,但可以通過並行處理他們解決您的問題,給它一個試試你可以馬上愛上它

class Program 
{ 
    static void Main(string[] args) 
    { 
     DoSomethingFromIntert bar = new DoSomethingFromIntert(); 
     bar.Verify(@"id1", true); 
     bar.Verify(@"id2", false); 
     bar.Verify(@"id3", true); 
     bar.Verify(@"id4", false); 
     bar.Verify(@"id5", true); 
     bar.Verify(@"id6", false); 

     bar.Complete(); 

     Console.ReadLine(); 
    } 
} 

public class DoSomethingFromIntert 
{ 
    BlockingCollection<VerifySomethingFromInternet> toProcess = new BlockingCollection<VerifySomethingFromInternet>(); 
    ConcurrentBag<VerifySomethingFromInternet> workinglist = new ConcurrentBag<VerifySomethingFromInternet>(); 

    public DoSomethingFromIntert() 
    { 
     //init four consumers you may choose as many as you want 
     ThreadPool.QueueUserWorkItem(DoesWork); 
     ThreadPool.QueueUserWorkItem(DoesWork); 
     ThreadPool.QueueUserWorkItem(DoesWork); 
     ThreadPool.QueueUserWorkItem(DoesWork); 
    } 

    public void Verify(string param, bool flag) 
    { 
     //add to the processing list 
     toProcess.TryAdd(new VerifySomethingFromInternet(param, flag)); 
    } 

    public void Complete() 
    { 
     //mark producer as complete and let the threads exit when finished verifying 
     toProcess.CompleteAdding(); 
    } 

    bool RemoveIFTrueFromInternet(VerifySomethingFromInternet vsfi) 
    { 
     Console.WriteLine(String.Format("Identification : {0} - Thread : {1}", vsfi.Identification, Thread.CurrentThread.ManagedThreadId)); 
     // Do some blocking work at internet 
     return vsfi.IsRemovable; 
    } 

    private void DoesWork(object state) 
    { 
     Console.WriteLine(String.Format("total : {0}", toProcess.Count)); 
     foreach (var item in toProcess.GetConsumingEnumerable()) 
     { 
      //do work 
      if (!RemoveIFTrueFromInternet(item)) 
      { 
       //add to list if working 
       workinglist.TryAdd(item); 
      } 
      //no need to remove as it is removed from the list automatically 
     } 
     //this line will only reach after toProcess.CompleteAdding() and when items are consumed(verified) 
     Console.WriteLine(String.Format("total : {0}", toProcess.Count)); 
    } 
} 
在短期將開始驗證項目

爲你添加它們,並會保持在一個單獨的列表成功項目

編輯

GetConsumingEnumerable() foreach循環默認不結束它繼續等待,直到永遠被CompleteAdding()稱爲下一個元素。所以我在包裝類中添加了Complete()方法來完成驗證循環,一旦我們推送了所有元素。

這個想法是不斷添加驗證元素到類中,並讓消費者循環驗證它們中的每一個,一旦完成,所有元素都調用Complete()來知道消費者沒有更多要添加的元素,以便在列表爲空時可以終止foreach循環。

在您的代碼中,刪除元素不是性能的實際問題,而是驗證過程的同步循環(如果出現熱點)。從列表中刪除只需要幾ms的代價,但代碼的昂貴部分是blocking work at internet,所以如果我們能夠使它平行,我們可以減少一些寶貴的時間。

請注意初始化消費者線程的數量,但是我使用了線程池,但如果過度使用,仍可能會影響性能。所以根據機器能力決定一個數字,例如。數量或內核/處理器

更多關於BlockingCollection

+0

謝謝,我會檢查你的代碼。 –