使用BlockingCollection <>:OperationCanceledException,有更好的方法吗?

Kie*_*one 29 c# multithreading blockingcollection

我正在使用(坦率的)很棒的BlockingCollection<>类型,用于多线程,高性能的应用程序.

通过集合有很多吞吐量,在微观层面上它具有很高的性能.但是,对于每个"批次",它将始终通过标记取消令牌来结束.这导致在任何等待Take调用时抛出异常.这没关系,但是我会确定一个返回值或输出参数来表示它,因为a)异常有明显的开销,而b)在调试时,我不想手动关闭特定的break-on-exception例外.

实现似乎很激烈,理论上我认为我可以反汇编并重新创建我自己的版本,但没有使用异常,但也许有一种不太复杂的方式?

我可以null在集合中添加(或者如果不是,占位符)对象以表示进程应该结束,但是还需要有一种很好地中止的方法,即唤醒等待的线程并告诉他们某些事情已经发生了.

那么 - 替代收藏类型?重新创建自己的?有人滥用这个吗?

(某些上下文:BlockingCollection<>因为它优于手动锁定,因此它具有优势Queue.最好我可以告诉使用线程原语是极好的,在我的情况下,这里和那里几毫秒,最佳核心使用至关重要. )

编辑:我刚开了这个奖金. 我不相信Anastasiosyal的答案涵盖了我在评论中提出的问题.我知道这是一个棘手的问题.有人能协助吗?

Ana*_*yal 9

我猜你已经完成了自己,看看BlockingCollection的反映来源,不幸的是,当一个CancellationToken被传递到BlockingCollection并取消时,你会得到OperationCancelledException,如下图所示(有几个图像后的变通方法)

GetConsumingEnumerableTryTakeWithNoTimeValidation在BlockingCollection上调用,而BlockingCollection又引发了这个异常.

在此输入图像描述

解决方法#1

一种可能的策略是,假设您对生产者和消费者有更多控制权,而不是将取消令牌传递给BlockingCollection(这将引发此异常),您将取消令牌传递给您的生产者和消费者.

如果您的生产者没有生产并且您的消费者没有消费,那么您已经有效地取消了操作而没有引发此异常并且在您的BlockingCollection中传递CancellationToken.None.

特殊情况当BlockingCollection处于BoundedCapacity或Empty时取消

生产者被阻止:当达到BlockingCollection上的BoundedCapacity时,生产者线程将被阻止.因此,当尝试取消并且BlockingCollection处于BoundedCapacity时(这意味着您的消费者未被阻止但生产者因为无法向队列中添加任何其他项而被阻止),那么您将需要允许使用其他项目(一个对于每个生成器线程)将解除生成器的阻塞(因为它们在添加到blockingCollection时被阻止),反过来允许你的取消逻辑在生产者端启动.

消费者被阻止:当您的消费者因队列为空而被阻止时,您可以在阻止集合中插入一个空的工作单元(每个消费者线程一个),以便取消阻止消费者线程并允许您的取消逻辑启动消费者方面.

如果队列中有项目且未达到BoundedCapacity或Empty等限制,则不应阻止生产者和消费者线程.

解决方法#2

使用取消工作单位.

当您的应用程序需要取消时,那么您的生产者(可能只有1个生产者就足够而其他人只是取消生产)将产生一个取消工作单元(可能是null,因为您还提到或某些实现标记接口的类).当消费者使用这个工作单元并检测到它实际上是一个取消工作单元时,他们的取消逻辑就会起作用.要生产的取消工作单元的数量需要等于消费者线程的数量.

当我们接近BoundedCapacity时,需要谨慎,因为这可能是一些生产者被阻止的迹象.根据生产者/消费者的数量,您可以让消费者消费,直到所有生产者(但都已关闭).这确保了周围没有挥之不去的生产者.当只有一个生产者离开时,您的最后一个消费者可以关闭,生产者可以停止生产取消工作单位.