2012-04-16 116 views
21

有沒有辦法一次添加多個項目到ConcurrentBag,而不是一次一個?我沒有看到ConcurrentBag上的AddRange()方法,但有一個Concat()。然而,這不是爲我工作:ConcurrentBag - 添加多個項目?

ConcurrentBag<T> objectList = new ConcurrentBag<T>(); 

timeChunks.ForEach(timeChunk => 
{ 
    List<T> newList = Foo.SomeMethod<T>(x => x.SomeReadTime > timeChunk.StartTime); 
    objectList.Concat<T>(newList); 
}); 

此代碼中使用是在一個Parallel.ForEach(),但我把它改成上面,所以我可以解決它。變量newList的確有對象,但在objectList.Concat <>行之後,objectList始終有0個對象。 Concat <>不是那樣工作嗎?我需要使用Add()方法一次向ConcurrentBag添加項目嗎?

回答

5

是:)

Concat也許是Enumerable擴展之一。它不會向ConcurrentBag添加任何內容,它只是返回一些包含原始包的時髦對象,以及您試圖在其中添加的任何內容。

請注意Concat的結果不再是ConcurrentBag,因此您不想使用它。它是通用LINQ框架的一部分,可以組合不可變序列。當然,這個框架不會試圖將操作數的併發屬性擴展到結果,所以生成的對象不會很適合多線程訪問。

(基本上,Concat適用於ConcurrentBag,因爲它暴露IEnumerable<T>接口。)

19

Concat是由LINQ提供的擴展方法。這是一個不可變的操作,它返回另一個IEnumerable,它可以枚舉緊隨指定集合的​​源集合。它不以任何方式更改源集合。

您需要一次將您的物品添加到ConcurrentBag

3

我遇到了一個類似的問題,試圖並行處理較小的數據塊,因爲一個大塊超時了我用來訪問發送端數據的Web服務,但我不希望事情運行得慢一些通過串行處理每個塊。通過記錄處理數據記錄的速度更慢 - 因爲我所調用的服務可以處理批量請求,所以最好在不超時的情況下提交儘可能多的數據。

就像Vlad說的那樣,將一個併發包放入一個對象類型列表不會返回一個併發包,所以concat不起作用! (我花了一段時間才意識到我無法做到這一點。)

試試這個,而不是 - 創建一個List<T>,然後創建一個ConcurrentBag<List<T>>。在每個並行迭代中,它將爲併發包添加一個新列表。並行循環完成後,循環通過ConcurrentBag和concat(或聯合,如果您想消除可能的重複項)到您創建的第一個List<T>,以將所有內容「扁平化」爲一個列表。

+0

最後使用SelectMany。 – 2018-02-08 17:06:31

17

(我知道這是一箇舊帖子,以爲我會添加一些東西)。

像其他人一樣說:是的,你需要逐一添加它們。就我而言,我增加了一個小的擴展方法做事情有點清潔,但引擎蓋下它做同樣的事情:

public static void AddRange<T>(this ConcurrentBag<T> @this, IEnumerable<T> toAdd) 
    { 
     foreach (var element in toAdd) 
     { 
      @this.Add(element); 
     } 
    } 

然後:

ConcurrentBag<int> ccBag = new ConcurrentBag<int>(); 
    var listOfThings = new List<int>() { 1, 2, 4, 5, 6, 7, 8, 9 }; 
    ccBag.AddRange(listOfThings); 

我還介紹了使用進行AsParallel要在擴展方法中添加,但在運行一些添加不同大小字符串列表的測試之後,使用AsParallel(如此處所示)與傳統的for循環相反要慢得多。

public static void AddRange<T>(this ConcurrentBag<T> @this, IEnumerable<T> toAdd) 
    { 
     toAdd.AsParallel().ForAll(t => @this.Add(t)); 
    }