【问题标题】:Threading and iterating through changing collections通过更改集合进行线程化和迭代
【发布时间】:2010-06-10 05:30:50
【问题描述】:
在 C#(控制台应用程序)中,我想保存一组对象。所有对象都属于同一类型。
我想遍历调用每个对象的方法的集合。然后不断重复这个过程。
然而,在迭代期间,可以从列表中添加或删除对象。 (对象本身不会被破坏......只是从列表中删除)。
不确定使用 foreach 循环 .. 或其他类似方法会发生什么。
这必须在之前完成 1000 次......你能推荐一个可靠的方法吗?
【问题讨论】:
标签:
multithreading
collections
【解决方案1】:
还有基于副本的方法。
算法是这样的:
- 锁定共享集合
- 将所有项目从共享集合复制到某个本地集合
- 释放共享集合上的锁定
- 迭代本地集合中的项目
这种方法的优点是您可以在很短的时间内锁定共享集合(假设共享集合相对较小)。
如果您要在每个集合项上调用的方法需要相当长的时间才能完成或可能会阻塞,那么在共享锁下迭代的方法可能会导致阻塞想要从共享集合中添加/删除项目的其他线程
但是,如果您要在每个对象上调用的方法相对较快,那么在共享锁下迭代可能会更可取。
【解决方案2】:
这是多线程同步的经典案例。
只有可靠的方法和更好的方法是在循环和从列表中添加/删除项目之间进行同步。
意味着您应该只在结束结束和迭代循环开始时允许添加/删除!
类似这样的事情:-
ENTER SYNC_BLOCK
WAIT FOR SYNC_BLOCK to be available
LOOP for items/ call method on them.
LEAVE SYNC_BLOCK
ENTER SYNC_BLOCK
WAIT FOR SYNC_BLOCK to be available
Add/Delete items
LEAVE SYNC_BLOCK
【解决方案3】:
当我读到这个例子时,我想到的是你可以使用C5TreeSet/TreeBag。它确实需要有一种方法来订购您的物品,但Tree 集合的优点是它们提供了一个Snapshot 方法(C5.IPersistentSorted 的成员),允许您制作轻量级的快照集合的状态,无需完全复制。
例如:
using(var copy = mySet.Snapshot()) {
foreach(var item in copy) {
item.DoSomething();
}
}
C5 还提供了一种“适用于所有人”的简单方法,并且与 .NET 2.0 兼容:
using(var copy = mySet.Snapshot()) {
copy.Apply(i => i.DoSomething());
}
请务必注意,应释放快照,否则您将对基础集合的后续修改产生小的性能损失。
这个例子来自非常彻底的C5 Book。