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(...)