从BlockingCollection<>中删除特定项目

时间:2011-09-22 17:01:20

标签: .net collections concurrency

有没有办法从BlockingCollection中删除特定的项,如下所示:

IMyItem mySpecificItem = controller.getTopRequestedItem();
bool took = myBlockingCollection<IMyItem>.TryTake(out mySpecificItem);
if(took)
  process(mySpecificItem);
.....

换句话说:我想从BlockingCollection&lt;&gt;中删除一个项目,该项目由某个字段(例如ID)标识,而不仅仅是下一个项目可用。

我是否需要实现getHashCode()或IComparer?

2 个答案:

答案 0 :(得分:7)

BlockingCollection<>在这里不会帮到你。我认为你需要ConcurrentDictionary<>

答案 1 :(得分:1)

我认为这里直接问题的答案是&#34;不,没有&#t;#34;。但我有相同的潜在场景问题。以下是我解决它的方法。

TL; DR我使用了一个BlockingCollections数组,每个数组元素用于不同的优先级。这似乎是最简单的解决方案。它完全适合可用的BlockingCollection方法。

我遇到了同样的挑战:偶尔会添加更高优先级元素的队列。当存在更高优先级的元素时,需要在所有普通优先级元素之前处理它们。较高优先级元素和普通优先级元素需要分别在其优先级桶内以FIFO顺序处理。为了灵活处理如何使用处理,理想情况下优先级不应该是有限的小集合,但正如您在下面看到的那样,我最终会因此而妥协。

我最终得到的代码: 在课堂申报中。这假设我们需要4个优先级:

BlockingCollection<ImageToLoad>[] imagesToLoad = new BlockingCollection<ImageToLoad>[4];

在类构造函数中(使用花哨的LINQ语法创建4个BlockingCollection实例 - 当它写成0-&gt; 3 for loop :-)时,坦率地说它更容易阅读):

imagesToLoad = Enumerable.Repeat(0, 4).Select(bc => new BlockingCollection<ImageToLoad>()).ToArray();

将元素添加到工作队列时:

imagesToLoad[priority].Add(newImageToLoad);

工作人员任务,挑选具有高prio的任务(4比prio高于3等),并且当工作到达4个prio队列中的任何一个时从其无工作排队的块中醒来:

while (true)
{
    ImageToLoad nextImageToLoad = null;
    for (int i = 3; i >= 0; i--)
        if (imagesToLoad[i].TryTake(out nextImageToLoad))
            break;
    if (nextImageToLoad == null)
        BlockingCollection<ImageToLoad>.TakeFromAny(imagesToLoad, out nextImageToLoad);
    if (nextImageToLoad != null)
        await LoadOneImage(nextImageToLoad);
}

由FIFO ConcurrentQueue支持的BlockingCollection是一个很好的基础。它允许您执行FirstOrDefault(Func谓词)来查找具有更高优先级的项目。但是,正如在这个问题中所提到的,当它们被无序处理时,它没有一个Remove(Func谓词)或Take(Func谓词)方法来从队列中获取更高优先级的项目。

我考虑了4个解决方案,以减少工作量和复杂性: A.编写我自己的BlockingCollection版本。这是最优雅的解决方案。当你只需要Add,Take and Take(Func谓词)时,我不认为复杂性会太复杂,但它需要时间并且至少会出现一些并发或锁定错误这将很难找到。我会使用SemaphoreSlim来处理阻塞,同时等待新元素到达。我可能会使用OrderedDictionary来保存元素。 B.放弃BlockingCollection。使用OrderedDictionary&lt;&gt;直接(类似于David在上面用ConcurrentDictionary建议的内容)。逻辑与选项A非常相似,只是没有封装在单独的类中。 C.这是一种破解:向正在排队的元素添加属性,以指示元素是否已被处理。当您因为具有更高优先级而无序处理元素时,请将此属性设置为&#34;已经完成了此属性&#34;。当你以后拿这个元素因为它恰好成为队列前面的元素时,只需丢弃它(而不是处理它,就像你对队列前面的元素所做的一样,这是一个避难所。已经处理完毕)。然而,这变得复杂,因为:(1)你最终不得不在很多情况下特别处理那些并非真正属于BlockingCollection的元素。 (2)从并发角度看,混淆部分依赖于BlockingCollection和BlockingCollection.Take()内部的锁,部分依赖于我需要添加的锁来执行围绕查找高prio项并更新它们的逻辑。由于两个独立锁之间的冲突,它很容易导致死锁。 D.不要让优先级数量为开放式,而是将其锁定为2或3,或者我认为可能需要的任何内容,并创建许多单独的BlockingCollections。然后,您可以使用TakeFromAny调用进行阻止。如果没有阻止(因为当你准备处理另一个元素时已有元素可用),你可以检查每个BlockingCollection和Take优先级最高的那个。

我花了很多时间试图让选项(C)工作,然后才意识到它正在创造的所有复杂性。

我最终选择了(D)。

直接将优先级数量设置为动态,以便在创建类时设置它。但是使用优先级作为任何int的完全运行时动态是不实用的,因为会创建很多未使用的BlockingCollection对象。