C# 与Queue.Peek()类似,用于在侦听消费IEnumerable时阻止收集<;T>;

C# 与Queue.Peek()类似,用于在侦听消费IEnumerable时阻止收集<;T>;,c#,multithreading,wcf,.net-4.0,task-parallel-library,C#,Multithreading,Wcf,.net 4.0,Task Parallel Library,我正在使用实现将消息消费者与生产者分离,以避免缓慢的消费者问题 如果消息处理阶段出现任何异常,它将丢失,并且不会发送到其他服务/层[2]。我如何在[3]中处理此类问题,这样信息就不会丢失,重要的是什么!消息的顺序不会混淆,因此上层服务/层将按照消息的出现顺序获取消息。我有一个想法,涉及到另一个中间队列,但它似乎很复杂?不幸的是,BlockingCollection没有公开任何类似的Queue.Peek()方法,因此我可以读取下一条可用消息,如果处理成功,请执行Dequeue() EDIT3:我当

我正在使用实现将消息消费者与生产者分离,以避免缓慢的消费者问题

如果消息处理阶段出现任何异常,它将丢失,并且不会发送到其他服务/层
[2]
。我如何在
[3]
中处理此类问题,这样信息就不会丢失,重要的是什么!消息的顺序不会混淆,因此上层服务/层将按照消息的出现顺序获取消息。我有一个想法,涉及到另一个中间
队列
,但它似乎很复杂?不幸的是,
BlockingCollection
没有公开任何类似的
Queue.Peek()
方法,因此我可以读取下一条可用消息,如果处理成功,请执行
Dequeue()

EDIT3:我当然可以使用老式的方法,使用while循环、bool标志、
队列和
自动resetEvent
,但我只是想知道是否可以使用
BlockingCollection
getconsumineGenumerable()
这样的工具 与消费enumerable一起使用时非常有用,因为所有管道模式实现示例都是新的东西,如
BlockingCollection
GetConsumingEnumerable()
看起来不持久,我必须回到旧方法。

您可以使用,它有方法

ConcurrentQueue.TryDequeue(out T result)
尝试删除并返回并发队列开头的对象,如果从ConcurrentQueue开头成功删除并返回元素,则返回true

所以,不需要先看一眼

TryDequeue()
是线程安全的:

ConcurrentQueue
在内部处理所有同步。如果两个线程恰好在同一时刻调用TryDequeue(T),则两个操作都不会被阻止

据我所知,仅当队列为空时才会返回false

如果队列中填充了q.Enqueue(“a”)等代码;q、 排队;q、 排队;两个线程同时尝试将一个元素出列,一个线程将a出列,另一个线程将b出列。对TryDequeue(T)的两个调用都将返回true,因为它们都能够使元素出列。如果每个线程返回一个额外的元素出列,其中一个线程将出列c并返回true,而另一个线程将发现队列为空并返回false

更新


也许,最简单的选择是使用类。有了它,你就可以把所有的处理任务打包到队列的项目中,简化同步的实现。

你应该考虑中间队列。

BlockingCollection
由于其性质,无法“偷看”项目-可能有多个消费者。他们中的一个可以偷看一个项目,另一个可以拿走它——因此,第一个会尝试拿走已经拿走的项目。

正如他在评论中所说,为接口的任何实现者提供了一个阻塞包装器

正如您所看到的,
IProducerConsumerCollection
在设计上没有定义
Peek
或实现它所需的其他方法。这意味着
BlockingCollection
目前无法为
Peek
提供分析

如果您认为,这将极大地减少由“代码”> PEEK < /代码>实现的实用性交易带来的并发问题。不消费怎么能消费?要同时进行

Peek
,您必须锁定收藏的头部,直到
Peek
操作完成,我和
BlockingCollection
的设计者认为该操作是次优的。我认为它也会很混乱,很难实现,需要某种一次性的peek上下文

如果您使用了一条消息,但它的使用失败,那么您将不得不处理它。您可以将其添加到另一个失败队列,将其重新添加到正常处理队列以供将来重试,或者只为后代记录其失败,或者执行适合您上下文的其他操作

如果您不想同时使用这些消息,则无需使用
BlockingCollection
,因为您不需要同时使用。您可以直接使用,您仍然可以从添加中获得同步性,并且您可以安全地使用,因为您可以控制单个消费者。如果消费失败,您可以使用您想要的无限重试循环来停止消费,不过,我建议这需要一些设计思想。

BlockingCollection
IProducerConsumerCollection
的包装器,它比例如
ConcurrentQueue
更通用,并使实现者不必实现
(Try)Peek
方法

但是,您始终可以直接在基础队列上调用
TryPeek

ConcurrentQueue<T> useOnlyForPeeking = new ConcurrentQueue<T>();
BlockingCollection<T> blockingCollection = new BlockingCollection<T>(useOnlyForPeeking);
...
useOnlyForPeeking.TryPeek(...)
ConcurrentQueue useOnlyForPeeking=新建ConcurrentQueue();
BlockingCollection BlockingCollection=新建BlockingCollection(仅用于查看);
...
仅用于查找。TryPeek(…)

但是请注意,您不能通过
仅用于查看
来修改队列,否则
阻止收集将变得混乱,并可能对您造成伤害,但是,如果在这个并发数据结构上调用非修改的
TryPeek
将是一个问题,我会感到惊讶。

您希望在重试时不会抛出异常吗?如果是,只需放置一个while循环,直到没有抛出异常为止。如果没有,您需要决定在无法处理项目的情况下如何处理。可能会将错误消息传递到下一层?这是服务器WCF服务,它向客户端代理发送消息,可以
bool successfullySent = true;
try
{
   var item = queue.Peek();
   PreProcessItem(item);
   SendItem(item);       
}
catch(Exception exception)
{
   successfullySent = false;
}
finally
{
   if (successfullySent)
   {
       // just remove already sent item from the queue
       queue.Dequeue();
   }
}
ConcurrentQueue<T> useOnlyForPeeking = new ConcurrentQueue<T>();
BlockingCollection<T> blockingCollection = new BlockingCollection<T>(useOnlyForPeeking);
...
useOnlyForPeeking.TryPeek(...)