我发现自己对即将推出的.NET 4.0框架中存在ConcurrentBag<T>
类非常感兴趣:
当订购无关紧要时,包对于存储物品非常有用,与套装不同,包支持重复。
我的问题是:这个想法怎么可能实现?我熟悉的大多数集合基本上等于(在引擎盖下)某种形式的数组,其顺序可能不“重要”,但是一个顺序(这就是为什么,即使它没有不需要,枚举几乎总是经历一个未更改的集合,无论是List
,Queue
,Stack
等,在同一序列中。)
如果我不得不猜测,我可能会在内部暗示它可能是Dictionary<T, LinkedList<T>>
;但实际上这似乎非常可疑,因为只使用任何类型T
作为关键字是没有意义的。
我期待/希望这是一个已经在某个地方被“弄清楚”的既定对象类型,并且知道这种已建立类型的人可以告诉我它。这对我来说太不寻常了 - 其中一个概念在现实生活中很容易理解,但很难转化为可用的类作为开发人员 - 这就是为什么我对可能性感到好奇。
修改:
一些响应者建议Bag
可以是内部哈希表的一种形式。这也是我最初的想法,但我预见到这个想法有两个问题:
正如Meta-Knight建议的那样,也许一个例子可以说明这一点:
public class ExpensiveObject() {
private ExpensiveObject() {
// very intense operations happening in here
}
public ExpensiveObject CreateExpensiveObject() {
return new ExpensiveObject();
}
}
static void Main() {
var expensiveObjects = new ConcurrentBag<ExpensiveObject>();
for (int i = 0; i < 5; i++) {
expensiveObjects.Add(ExpensiveObject.CreateExpensiveObject());
}
// after this point in the code, I want to believe I have 5 new
// expensive objects in my collection
while (expensiveObjects.Count > 0) {
ExpensiveObject expObj = null;
bool objectTaken = expensiveObjects.TryTake(out expObj);
if (objectTaken) {
// here I THINK I am queueing a particular operation to be
// executed on 5 separate threads for 5 separate objects,
// but if ConcurrentBag is a hashtable then I've just received
// the object 5 times and so I am working on the same object
// from 5 threads at the same time!
ThreadPool.QueueUserWorkItem(DoWorkOnExpensiveObject, expObj);
} else {
break;
}
}
}
static void DoWorkOnExpensiveObject(object obj) {
ExpensiveObject expObj = obj as ExpensiveObject;
if (expObj != null) {
// some work to be done
}
}
答案 0 :(得分:9)
如果您查看ConcurrentBag<T>
的详细信息,您会发现它在内部基本上是一个自定义链接列表。
由于Bags可以包含重复项,并且索引无法访问,因此双向链表是实现的一个非常好的选择。这允许锁定对于插入和移除而言相当精细(您不必锁定整个集合,只需要锁定插入/移除的位置周围的节点)。由于您不担心重复,因此不涉及散列。这使得双链表更加完美。
答案 1 :(得分:2)
这里有一些关于ConcurrentBag的好消息:http://geekswithblogs.net/BlackRabbitCoder/archive/2011/03/03/c.net-little-wonders-concurrentbag-and-blockingcollection.aspx
ConcurrentBag的工作方式 是利用新的 ThreadLocal类型(新的 System.Threading for .NET 4.0)这样 每个使用包的线程都有一个列表 只是那个线程的本地。
这意味着添加或删除 线程本地列表要求非常低 同步。问题来了 线程去哪里消耗一个项目 但它的本地列表是空的。在这 这个包执行“偷工作”的情况 它会抢夺另一个物品 在其列表中包含项目的线程。 这需要更高的水平 同步增加了一点 采取操作的开销。
答案 2 :(得分:0)
由于排序无关紧要,ConcurrentBag可能会在幕后使用哈希表来快速检索数据。但与Hashset不同,一个包接受重复。也许每个项目都可以与Count属性配对,当添加项目时,该属性设置为1。如果您再次添加相同的项目,则可以只增加此项目的Count属性。
然后,要删除计数大于1的项目,您可以减少此项目的计数。如果计数为1,则将从哈希表中删除Item-Count对。
答案 3 :(得分:0)
好吧,在smalltalk(Bag的概念来自)中,该集合基本上与哈希相同,尽管它允许重复。它不是存储重复的对象,而是保持“发生计数”,例如每个对象的引用计数。如果ConcurrentBag是一个忠实的实现,这应该给你一个起点。
答案 4 :(得分:0)
我相信'Bag'的概念与'Multiset'同义。
如果您对它们的实现方式感兴趣,有许多“Bag”/“Multiset”实现(这些都是java),它们是开源的。
这些实施表明,“Bag”可以根据您的需要以多种方式实施。有TreeMultiset,HashMultiset,LinkedHashMultiset,ConcurrentHashMultiset的例子。
Google Collections
Google有一些"MultiSet" implementations,其中一个是ConcurrentHashMultiset。
Apache Commons
Apache有许多“Bag”实现。