在对象的ConcurrentBag上枚举和修改的最有效方法



我有一个对象的ConcurrentBag,我想对它执行以下操作:

  1. 使用where筛选枚举所有项目
  2. 对于每个项,检查一些属性,并根据值进行一些方法调用。在方法调用之后,最好从包中移除项目
  3. 修改某些属性的值并将其保存到包中

所以基本上我需要以下内容:

foreach (var item in myBag.Where(it => it.Property1 = true))
{

if (item.Property2 = true)
{
SomeMethodToReadTheItem(item);
//it's better to remove this item from the bag here, but 
//there is a permeance hit, then just leave it.
}
else
{
item.Property3=  "new value";
//now how do I save the item back to the bag?
}

}

当然,它应该以线程安全的方式进行。我知道ConcurrentBag上的枚举实际上是在";快照";真正的袋子,但用where子句过滤器怎么样?我应该做一个ToList来防止它生成一个新的";快照";?另外,如果你想修改一个特定的项目,你只需要打包。TryTake(取出项目(。但是既然我已经得到了枚举中的项,我应该"取";又来了?

任何解释/评论/样本都是事先准备好的。

谢谢。

我将尝试在不涉及性能的情况下回答您问题的特定部分。

首先,Where方法将IEnumerable<T>作为其第一个参数,并将自己迭代枚举对象,该枚举对象将调用GetEnumerator()一次,因此您将只获取底层ConcurrentBag的一个快照。

其次,代码的线程安全性不是很清楚,在代码的其余部分可能有一些未指定的隐含保证。例如,您有一个ConcurrentBag,因此您的集合是线程安全的,但是您可以在没有任何线程同步的情况下修改该集合中包含的项目。如果有其他代码运行相同的方法,或者在另一个方法中同时读取/修改ConcurrentBag中的项,那么您可能会看到数据争用。

请注意,如果您已经有对该项的引用,则不必调用TryTake,因为它只会返回相同的引用。

我建议您只创建一个新列表,如果使用WHERE筛选器,则将其添加到这个新列表中。它看起来像这样:

List<T> myNewList = new List<T>();
foreach (var item in myBag.Where(it => it.Property1))
{
if (!item.Property2)
{
myNewList.Add(item);
}            
}

注意"quot;

最新更新