【发布时间】:2014-10-01 09:43:15
【问题描述】:
考虑以下函数,它遍历通用 List<T>: Items,并在找到匹配项时更改匹配项:
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。
这是以线程安全的方式修改集合的明智方式吗?
【问题讨论】:
-
如果您的
ID是独一无二的,我会说ConcurrentDictionary更合适。不,您的第二种方法不是线程安全的,因为您没有使用任何锁,并且正在获取物品,创建临时包等,如果同时发生,所有这些都可能导致问题。 -
@Maarten 谢谢 - ID 并不总是唯一的,因为根据项目中使用的类,未保存的条目的 id 为 0。另外,我认为 Concurrent 命名空间使用了自己的内部锁定方法。我的函数还使用了我认为的从 ConcurrentBag 中删除项目的线程安全方法:TryTake()?
-
如果您希望某些东西被称为 线程安全,那么 您的操作作为一个整体 必须是线程安全的,而且事实上您使用线程安全的方法来移除一个项目是不够的。
-
即使您对集合本身的操作发生了改变,使得使用该集合的其他线程不会破坏它,您仍然必须处理您正在改变访问的项目这一事实来自多个线程,这并不安全。
标签: c# multithreading collections