如何实现像.NET的ConcurrentBag <t>这样的类?</t>

时间:2009-11-06 16:50:20

标签: .net language-agnostic collections bag

我发现自己对即将推出的.NET 4.0框架中存在ConcurrentBag<T>类非常感兴趣:

  

当订购无关紧要时,包对于存储物品非常有用,与套装不同,包支持重复。

我的问题是:这个想法怎么可能实现?我熟悉的大多数集合基本上等于(在引擎盖下)某种形式的数组,其顺序可能不“重要”,但一个顺序(这就是为什么,即使它没有不需要,枚举几乎总是经历一个未更改的集合,无论是ListQueueStack等,在同一序列中。)

如果我不得不猜测,我可能会在内部暗示它可能是Dictionary<T, LinkedList<T>>;但实际上这似乎非常可疑,因为只使用任何类型T作为关键字是没有意义的。

我期待/希望这是一个已经在某个地方被“弄清楚”的既定对象类型,并且知道这种已建立类型的人可以告诉我它。这对我来说太不寻常了 - 其中一个概念在现实生活中很容易理解,但很难转化为可用的类作为开发人员 - 这就是为什么我对可能性感到好奇。

修改

一些响应者建议Bag可以是内部哈希表的一种形式。这也是我最初的想法,但我预见到这个想法有两个问题:

  1. 当您对相关类型没有合适的哈希码函数时,哈希表并不是那么有用。
  2. 只是跟踪对象在集合中的“计数”与存储对象不同。
  3. 正如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
        }
    }
    

5 个答案:

答案 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”实现。