我在ConcurrentBag
内有Parallel.ForEach
个读/写操作。基本上,我需要根据几个属性来检查包中是否存在物体,如果没有匹配,那么我将它添加到包中。这真的非常慢。使用没有锁定的List<>
只是一小部分时间。这段代码有什么问题?我最好使用ReaderWriterLockSlim
列表锁定吗?我在这里处理大约1,000,000个对象。
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);
}
}
答案 0 :(得分:3)
ConcurrentBag<T>
并未针对此类情景进行优化。它是使用ThreadLocal<T>
实现的,这会使您的特定用例变慢。您反复在许多线程上迭代整个集合。迭代整个集合以检查对象是否存在也很慢。
我建议重载Beneficiary.GetHashCode
并使用ConcurrentDictionary<Beneficiary, byte>
。字节值可以被忽略,它实际上是一个并发的hashset。
答案 1 :(得分:3)
首先,你所拥有的解决方案根本不是安全的。在您复制副本时,甚至在您执行Any
之后但在致电Add
之前,可以将项目添加到集合中。您还在进行线性搜索,这根本不会表现良好。使用基于字典的结构可以更快地查找,并且还需要确保您在此处拥有的整个方法在逻辑上是原子的。
你可以做的是使用ConcurrentDictionary
并创建一个IEqualityComparer
来检查你关心的5个属性,这样你就可以在覆盖重复项时将这些项添加到字典中。
当然,如果创建每个对象实际上需要大量工作,这只是有意义的。如果你所要做的就是获得一系列不同的项目,那么尝试并行化该操作的可能性就不会是一场胜利。如果这基本上只是你所做的事情,那么每个线程需要完成其工作的资源将会出现这样的争论,即你所拥有的实际并行化的数量将非常低,几乎可以肯定比线程开销少的方法会花费你。您可能最好只使用同步Distinct
来电。
答案 2 :(得分: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
访问不同的受益人。
注意:你应该替换&#34;对象&#34;按typeOf ProductType键入。