如何判断特定元素是否存在于BlockingCollection中?
嘿,这个问题我之前做并发队列处理时也遇到过!BlockingCollection确实没提供直接的Contains方法,不过我们有几种靠谱的替代方案,根据你的场景选就行:
BlockingCollection本质是对IProducerConsumerCollection<T>的包装,你可以通过它的Collection属性拿到底层的集合实例。如果你的底层集合(比如ConcurrentBag<T>、ConcurrentDictionary<T, object>这类)自带Contains方法,那就直接用:
// 初始化时指定带Contains方法的底层集合,比如ConcurrentBag private readonly BlockingCollection<ImageKeys> myCompletedImages = new BlockingCollection<ImageKeys>(new ConcurrentBag<ImageKeys>()); public void addItem(ImageKey theImagekey) { // 直接调用底层集合的Contains判断 if (this.myCompletedImages.Collection.Contains(theImagekey)) { return; } this.myCompletedImages.Add(theImagekey); }
⚠️ 注意:如果你的底层集合是默认的ConcurrentQueue<T>或者ConcurrentStack<T>,它们并没有Contains方法,这个方案就不适用了。
这是我最常用的方案,因为.NET没有原生的ConcurrentHashSet,但我们可以用ConcurrentDictionary<T, bool>来模拟——键存你的ImageKey,值随便存个true就行。这种方式判断存在性是O(1)的性能,而且完全线程安全:
private readonly BlockingCollection<ImageKeys> myCompletedImages = new BlockingCollection<ImageKeys>(); // 用ConcurrentDictionary模拟线程安全的哈希集合 private readonly ConcurrentDictionary<ImageKeys, bool> _trackedKeys = new ConcurrentDictionary<ImageKeys, bool>(); public void addItem(ImageKey theImagekey) { // TryAdd会在键不存在时添加并返回true,存在则返回false if (!_trackedKeys.TryAdd(theImagekey, true)) { return; } // 只有当键是新的时,才加入BlockingCollection this.myCompletedImages.Add(theImagekey); }
💡 额外提醒:如果你后续会从myCompletedImages中取出元素处理,记得同时从_trackedKeys里移除对应的键,不然字典会一直占用内存,导致内存泄漏。
如果你的集合元素很少,偶尔用用这个方法也可以,但大集合绝对不推荐——因为它需要遍历所有元素,时间复杂度是O(n),性能很差:
public void addItem(ImageKey theImagekey) { // 先获取当前集合的快照(ToArray会复制一份元素),再判断是否存在 if (myCompletedImages.ToArray().Contains(theImagekey)) { return; } myCompletedImages.Add(theImagekey); }
⚠️ 注意:这里用ToArray()是为了避免遍历过程中集合元素变化导致的异常,但快照和真实集合可能存在短暂的不一致,需要你能接受这种微小的时间差。
内容的提问来源于stack exchange,提问作者Mr.Curious

