2014-10-01 47 views
1

考虑下面的函数,它迭代通用List<T>:项目,并改变匹配的项目,如果发现:用于修改ConcurrentBag中项目的可接受方法?

void UpdateList(ref List<clsMyClass> Items, int idToFind) { 
    foreach(var Item in Items) { 
     if (Item.ID == idToFind) 
     { 
      // modify the item 
      Item.SomeIntCounter++; 
      return; 
     } 
    } 
} 

现在,如果我想要做同样的事情,但使用一个线程安全的这个时候ConcurrentBag<T>,这是一个可以接受的方法?...

void UpdateList(ref ConcurrentBag<clsMyClass> Items, int idToFind) { 
    clsMyClass Item; 
    bool found = false; 
    ConcurrentBag<clsMyClass> tempItems = new ConcurrentBag<clsMyClass>(); 
    while(Items.Count > 0) { 
     if (Items.TryTake(out Item)) 
     { 
      if (Item.ID == idToFind) 
      { 
       //modify the item 
       Item.SomeIntCounter++; 
       found = true; 
      } 
      tempItems.Add(Item); 
      if (found) break; 
     } 
    } 
    foreach(var tempItem in tempItems) Items.Add(tempItem); 
} 

这里的想法是,每个项目从ConcurrentBag删除并添加到一个临时直到匹配的项目被发现,改变,在此之后所有拆下的项目被重新添加到ConcurrentBag中。

这是一个明智的方式来修改线程安全的方式收集?

+1

如果你的'ID'是唯一的,我会说'ConcurrentDictionary'更合适。不,你的第二种方法不是线程安全的,因为你没有使用任何锁,并且正在取物品,创建一个临时包等等,如果它们同时发生会造成问题。 – Maarten 2014-10-01 09:55:07

+0

@Maarten谢谢 - 根据项目中使用的类别,ID未必是唯一的,因为未保存的条目的ID为0。另外,我认为Concurrent命名空间使用了它自己的内部锁定方法。我的函数也使用我认为是从ConcurrentBag:TryTake()中删除项目的线程安全方法? – Panjo 2014-10-01 10:14:01

+1

如果你想要某个被称为*线程安全*的东西,那么**作为一个整体**的操作必须是线程安全的,并且使用线程安全的方法移除一个项目是不够的。 – Maarten 2014-10-01 10:34:10

回答

-1

您的并发版本UpdateList不是线程安全的,因为它引入了race condition

您的第一版UpdateList与多线程情况下的第二版不相同。你明白为什么?如果你启动两个线程执行UpdateList一个与idToFind_1和另一个与idToFind_2工作在相同的ConcurrentBag<T> Items。然后,第一个线程可能会取出第二个需要更新的项目。因此,具有idToFind_2的项目很可能会错过更新,反之亦然。在这里,我们有竞赛条件:如果线程1将该项目及时返回,它将得到更新否则它不会。

此外,你仍然要处理,你突变所从多个线程访问的项目的事实,因为它是不是安全(Servy的评论)。

由于实施在某种程度上效率低下。您是否考虑过使用另一个更合适的数据结构,并且可能会利用lock来关注同步,其他任何代码块都使用该代码块来提供对数据结构的相同实例的独占访问。

此外,由于tempItems位于UpdateList的本地,因此您不需要线程安全的集合,因为没有同步。所以一个简单的List<T>就足够了。

您不需要参数ref关键字,请参阅When to use ref and when it is not necessary in C#

+1

OP的代码*不安全。只有在访问这些数据的其他代码块都锁定在同一实例上时,'lock'才有用。如果没有'ref'关键字,对象将不会被引用传递。它会通过价值传递,但是这个价值会成为一个参考。这是一个非常重要的区别。 – Servy 2014-10-01 16:49:42