非常简单:除了ConcurrentDictionary(我将使用它,但它不是真正正确的概念),是否有任何Concurrent集合(IProducerConsumer实现)支持基于项目的简单相等删除特定项目或定义删除条件的谓词?
说明:我有一个多线程,多阶段的工作流算法,它从数据库中提取对象并将它们放在“起始”队列中。从那里他们被下一阶段抓住,进一步努力,并塞进其他队列。这个过程将持续几个阶段。同时,第一个阶段由其主管再次调用,并将对象拉出数据库,这些对象可以包括仍处于进行中的对象(因为它们尚未完成处理,因此没有使用标记集重新保留他们已经完成了。
我正在设计的解决方案是一个主要的“工作”集合;当对象在第一阶段被检索以进行处理时进入该队列,并且在它们被重新保存到DB之后被移除,因为工作流的任何阶段都已经“处理”完成了必要的处理。当对象在该列表中时,如果在第一阶段重新检索它,它将被忽略。
我原本计划使用ConcurrentBag,但唯一的删除方法(TryTake)会从包中删除任意项,而不是指定的项(并且.NET 4中的ConcurrentBag是慢)。 ConcurrentQueue和ConcurrentStack也不允许删除除了它给你的下一个项目之外的项目,留下ConcurrentDictionary,它可以工作但比我需要的更多(我真正需要的是存储正在处理的记录的Id;它们在工作流程中不会改变。)
答案 0 :(得分:17)
没有这种数据结构的原因是所有集合的查找操作时间都为O(n)
。它们是IndexOf
,Remove(element)
等。它们都枚举所有元素并检查它们是否相等。
只有哈希表的查找时间为O(1)。在并发场景中,O(n)查找时间将导致集合的锁定非常长。其他线程在此期间将无法添加元素。
在字典中,只有被哈希命中的单元格才会被锁定。当通过散列单元格中的元素检查相等性时,其他线程可以继续添加。
我的建议是继续使用ConcurrentDictionary。
顺便说一下,对于你的解决方案来说,ConcurrentDictionary有点过大了。你真正需要的是快速检查物体是否正在工作。 HashSet
对此非常适合。它基本上没有Add(element)
,Contains(element)
,Remove(element)
。 java中有一个ConcurrentHeshSet
实现。对于c#我发现了这个:How to implement ConcurrentHashSet in .Net不知道它有多好。
作为第一步,我仍然会在HashSet
周围编写一个带ConcurrentDictionary
接口的包装器,然后启动并运行,然后尝试不同的实现并查看性能差异。
答案 1 :(得分:5)
正如其他帖子所解释的那样,默认情况下无法从Queue
或ConcurrentQueue
删除项目,但实际上最简单的方法是扩展或包装项目。
public class QueueItem
{
public Boolean IsRemoved { get; private set; }
public void Remove() { IsRemoved = true; }
}
出局时:
QueueItem item = _Queue.Dequeue(); // Or TryDequeue if you use a concurrent dictionary
if (!item.IsRemoved)
{
// Do work here
}
答案 2 :(得分:1)
在一般意义上,确实难以使集合成为线程安全的。线程安全中有很多因素超出了库/框架类的责任或范围,影响了它真正“线程安全”的能力......你指出的一个缺点就是出来就是表现。编写一个也是线程安全的高性能集合是不可能的,因为它必须假设最糟糕的......
通常建议的做法是使用您想要的任何集合并以线程安全的方式访问它。这基本上就是为什么框架中没有更多的线程安全集合。有关这方面的更多信息,请访问http://blogs.msdn.com/b/bclteam/archive/2005/03/15/396399.aspx#9534371