2014-09-03 52 views
2

我有一個ConcurrentBag暴露給Parallel.ForEach內的讀/寫操作。基本上,我需要根據幾個屬性檢查包中是否存在對象,如果沒有匹配,則將其添加到包中。這真的很慢。沒有鎖的情況下使用List<>只是一小部分時間。這段代碼有什麼問題?我最好使用列表鎖定ReaderWriterLockSlim?我在這裏處理大約1,000,000個物體。Parallel ForEach和ConcurrentBag

var bag = new ConcurrentBag<Beneficiary>(); 

Parallel.ForEach(cx, _options, line => 
{ 
if (!bag.Any(o => 
     o.WinID == beneficiary.WinID && 
     o.ProductType == beneficiary.ProductType && 
     o.FirstName == beneficiary.FirstName && 
     o.LastName == beneficiary.LastName && 
     o.MiddleName == beneficiary.MiddleName)) 
{ 
     bag.Add(beneficiary);  
} 
} 
+2

如果代碼實際上沒有正常工作,則代碼的性能無關緊要。你真的很想要一個不正確的結果嗎?或者一個正確的結果會更慢?不是說沒有辦法改善這個,只是「螺紋安全」不是它的方法 – Servy 2014-09-03 18:02:06

回答

3

A ConcurrentBag<T>未針對此類情形進行優化。它通過使用ThreadLocal<T>來實現,這使您的特殊用例變得緩慢。您正在重複遍歷許多線程上的整個集合。迭代整個集合以檢查對象的存在也很慢。

我建議超載Beneficiary.GetHashCode並使用ConcurrentDictionary<Beneficiary, byte>。字節值可以被忽略,它實際上是一個併發的哈希集合。

+0

你也可以(使用ConcurrentDictionary)使用Tuple <>作爲鍵 – Seb 2014-09-03 18:21:55

+0

@Seb幫幫我? – Servy 2014-09-03 18:32:37

+0

什麼袋子有用?重載GetHashCode有什麼意義? – 2014-09-03 19:00:52

3

因此,首先,您擁有的解決方案根本不是安全的。可以在迭代副本時或者在執行Any之後,但在致電Add之前,將項目添加到集合中。你也在做一個線性搜索,它不會很好地執行。使用基於字典的結構可以更快地查找,而且還需要確保您在此處的整個方法在邏輯上是原子性的,這樣會更好。

你可以做的是使用ConcurrentDictionary並創建一個IEqualityComparer,檢查你關心的5個屬性,這將允許您在覆蓋重複項的同時將項添加到字典中。

當然,如果創建每個對象實際上需要大量工作,這些都是有意義的。如果你所要做的只是獲得不同項目的集合,那麼嘗試並行化該操作可能不會取得勝利。如果這基本上只是你所做的一切,那麼每個線程需要完成其工作的資源都會出現這樣的爭論:實際並行化的數量將非常低,幾乎可以肯定的是這比線程的開銷少花費你。相反,您可能會更好地使用同步呼叫Distinct

+0

包的目的是什麼?它應該用於什麼?順便說一句,這是處理約1,000,000個對象。看起來我需要一本字典。 – 2014-09-03 18:59:34

+1

@BigDaddy是的,我建議你使用基於散列的數據結構,即'ConcurrentDictionary',如果你真的需要並行地完成這項工作的話。正如我在我的回答中所說的那樣,*你所要做的工作不適合平行工作*。你可能會試圖平行化它,所以不要。這不會有幫助。 – Servy 2014-09-03 19:01:07

2

您可以使用Tuple作爲密鑰並使用ConcurrentDictionary來存儲您的benificiary對象。

var dict = new ConcurrentDictionary<Tuple<int, object, string>, Beneficiary>(); 

Parallel.ForEach(cx, _options, line => 
{ 
    string fullname = string.Join("|", line.FirstName, line.LastName, line.MiddleName); 

    Tuple<int, object, string> key = new Tuple<int,object,string>(line.WinID, line.ProductType, fullname); 

    //if (!dict.ContainsKey(key)) optional line 
    { 
     dict.TryAdd(key, line);} 
    } 
}); 

一旦parallel.ForEach完成後,您可以訪問使用簡單ForEach鮮明的受益者。

注意:您應該用typeOf ProductType替換「object」類型。

+0

你爲什麼要加入名稱,而不是使用'Tuple ? – svick 2014-09-11 18:53:53

+0

你的解決方案更好,更安全,但我有點懶。爲了可讀性,我儘量避免使用超過3個元素的元組。在這種特殊情況下的關鍵衝突是不可能的,所以我冒昧地加入了這些領域。問候。 – Seb 2014-09-11 23:30:00