C#:是否可以在不消耗/销毁元素的情况下枚举BlockingCollection(而不是请求枚举器时的快照)?

C#:是否可以在不消耗/销毁元素的情况下枚举BlockingCollection(而不是请求枚举器时的快照)?,c#,.net,multithreading,C#,.net,Multithreading,我需要使用一种ConsumerProducer模式(基于队列),在这种模式中,使用者线程实际上并不消耗(销毁)元素*,而是使用者线程可以在任何时候请求队列开头的句柄,并以自己的速度开始枚举。我不能使用ConcurrentQueue,因为枚举ConcurrentQueue可以在其快照上工作(在请求Enumerator时拍摄),因此使用者线程不会看到生产者同时添加的元素 在我看来,BlockingCollection应该是正确的类,但它显然只提供两种类型的枚举数/枚举数: GetConsumingE

我需要使用一种ConsumerProducer模式(基于队列),在这种模式中,使用者线程实际上并不消耗(销毁)元素*,而是使用者线程可以在任何时候请求队列开头的句柄,并以自己的速度开始枚举。我不能使用
ConcurrentQueue
,因为枚举
ConcurrentQueue
可以在其快照上工作(在请求
Enumerator
时拍摄),因此使用者线程不会看到生产者同时添加的元素

在我看来,
BlockingCollection
应该是正确的类,但它显然只提供两种类型的枚举数/枚举数:

GetConsumingEnumerable()
,其中

  • 销毁元素(调用出列)
  • 在真实版本的集合上工作
BlockingCollection.IEnumerable
,其中

  • 不会破坏元素
  • 处理在请求枚举数时拍摄的快照
事实上,
BlockingCollection
只提供这两种组合似乎太过严格,在我的场景中无法使用-我需要一些能够在真实队列上工作的东西(比如
consumineGenumerable
可以),但只枚举元素而不销毁(比如
BlockingCollection.IEnumerable
可以)

问题:

<> P>是否有人建议有一种方法来实现这个“强”,而不需要编写自己的队列实现<强>(这显然是我在讨论多线程环境时要做的一件相当令人沮丧的事情)——这可以通过<代码> BuffjCopys<代码>来实现吗?或者是否存在其他支持此类行为的集合


[(*)事实上,一个指定的使用者线程实际上有时可能会破坏元素(即调用
出列
)。但是,如果需要说此操作将使所有其他消费者的正在进行的枚举无效,我很好,因此可以将问题简化为假设消费者根本不退出队列。]

因为信息阻止收集是IProducerConsumerCollection上的一个外观,如果使用默认构造函数,实际上是一个ConcurrentQueue。因此,您的BlockingCollection将具有与基础Collections相同的限制。对于您的精确用例,我相信您应该能够通过采用并发链接列表并将其用作队列来做到这一点。BCL中没有实现,但有一些第三方的实现可用:请谨慎使用,我不知道它在性能方面有什么价值(或者即使它没有bug)@KevinGosse[1st comment]我知道,但遗憾的是,这似乎不是答案——正如在问题中所解释的,我对BlockingCollection在队列中添加的功能感兴趣(例如,GetConsumingEnumerable()是我迄今为止发现的最接近的东西——这是B.C.在IProducerConsumer公开的功能之上添加的东西)[2条评论]谢谢,我来看看。我认为这是一个标准用例(基本上是消费者/生产者,只有几个消费者能够消费相同的元素),标准库中应该有某种东西…?理解它为什么被故意破坏是非常重要的。枚举集合基本上是线程不安全的,类无法知道何时完成迭代,因此无法使用锁来确保在您忙碌时集合不会更改。制作副本是唯一合理的选择,但您的代码必须意识到它很容易过时。没有什么东西会爆炸得很厉害,但是当你的代码咀嚼旧数据时,它很可能会出故障。唯一好的选择是不需要迭代。@hans passant我对这个解释感到相当困惑——ConcurrentQueue和BlockingCollection之间的所有区别(ConcurrentQueue是前者在您提到的方式上被削弱了,而后者则不是(它提供了GetConsumingEnumerable(),它不创建shapshot)。我看到的所有可能的问题(包括您提到的问题,即迭代结束时的问题)都是相同的,无论您是想使用GetConsumingEnumerable()还是标准enumerable进行迭代,所以我不确定为什么B.C.会需要这种削弱。还是我误解了您的意思?