Удаление определенного элемента из коллекции BlockingCollection <> - PullRequest
5 голосов
/ 22 сентября 2011

Есть ли способ удалить определенный элемент из коллекции BlockingCollection, например:

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

другими словами: я хочу удалить элемент из коллекции BlockingCollection <>,который идентифицируется некоторым полем (например, ID), а не только следующим доступным элементом.

Нужно ли мне реализовывать getHashCode () или IComparer?

Ответы [ 2 ]

7 голосов
/ 22 сентября 2011

BlockingCollection<> не поможет вам здесь. Я думаю, что вам нужно ConcurrentDictionary<>.

1 голос
/ 15 апреля 2018

Я думаю, что ответ на прямой вопрос здесь "нет, нет". Но у меня была та же основная проблема сценария. Вот как я это решил.

TL; DR Я использовал массив BlockingCollections, где каждый элемент массива имеет свой уровень приоритета. Это кажется самым простым решением. Идеально подходит для доступных методов BlockingCollection.

У меня была такая же проблема: очередь, в которую иногда добавляются элементы с более высоким приоритетом. Когда есть элементы с более высоким приоритетом, они должны обрабатываться перед всеми элементами с обычным приоритетом. Как элементы с более высоким приоритетом, так и элементы с обычным приоритетом должны обрабатываться соответственно в порядке FIFO в пределах их приоритетных сегментов. Для гибкости в использовании обработки уровни приоритетов в идеале не должны быть конечным небольшим набором, но, как вы увидите ниже, я в итоге пошел на компромисс в этой необходимости.

Код, с которым я закончил: В объявлении класса. Это предполагает, что нам нужно 4 уровня приоритета:

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

В конструкторе класса (чтобы создать 4 экземпляра BlockingCollection с причудливым синтаксисом LINQ - откровенно проще читать, когда просто записывается как цикл 0-> 3 для :-)):

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);
}

BlockingCollection, поддерживаемая FIFO ConcurrentQueue, является отличной основой для этого. Это позволяет вам сделать FirstOrDefault (предикат Func), чтобы найти элементы с более высоким приоритетом. Но, как уже упоминалось в этом вопросе, в нем нет методов Remove (предикат Func) или Take (предикат Func), чтобы вывести элементы с более высоким приоритетом из очереди, когда они были обработаны не по порядку.

Я рассмотрел 4 решения в порядке убывания работы и сложности: А. Написание моей собственной версии BlockingCollection. Это самое элегантное решение. Я не думаю, что это будет слишком плохо по сложности, когда вам нужны только Add, Take и Take (предикат Func), но это займет время, и будут по крайней мере некоторые ошибки параллелизма или блокировки, которые будет трудно найти , Я бы использовал SemaphoreSlim для обработки блокировки во время ожидания поступления новых элементов. Я бы, вероятно, использовал OrderedDictionary, чтобы сохранить элементы. Б. Откажись от BlockingCollection. Используйте OrderedDictionary <> напрямую (аналогично тому, что Дэвид предложил выше с ConcurrentDictionary). Логика была бы очень похожа на вариант А, только не заключенная в отдельный класс. C. Это что-то вроде хака: добавьте свойство к элементам, попадающим в очередь, чтобы указать, был ли элемент уже обработан. Когда вы обрабатываете элемент не по порядку из-за его более высокого приоритета, установите для этого свойства значение «уже сделано с этим». Когда вы позже возьмете этот элемент, потому что он стал тем, что находится в начале очереди, просто отбросьте его (вместо того, чтобы обрабатывать его, как вы сделали бы с элементами, которые вы берете в начале очереди, которые еще не были обработанный). Это становится сложным, потому что: (1) вам в конечном итоге придется обрабатывать эти элементы, которые на самом деле не входят в BlockingCollection, особенно во многих контекстах. (2) путаница с точки зрения параллелизма, чтобы частично полагаться на блокировки внутри BlockingCollection и BlockingCollection.Take (), а частично на блокировки, которые мне нужно добавить, чтобы выполнить логику поиска элементов высокого уровня и их обновления. Это будет слишком легко привести к тупикам из-за конфликтов между двумя отдельными блокировками.D. Вместо того, чтобы разрешить открывать число уровней приоритета, установите для него значение 2 или 3, или, как мне кажется, мне может понадобиться, и создайте такое количество отдельных BlockingCollections. Затем вы можете сделать блокировку с помощью вызова TakeFromAny. Если блокирование не выполняется (поскольку элементы уже доступны, когда вы готовы обработать другой), вы можете проверить каждую коллекцию BlockingCollection и Take из элемента с наивысшим приоритетом, который не пуст.

Я потратил немало времени, пытаясь заставить работать опцию (C), прежде чем осознал всю сложность, которую он создавал.

В итоге я выбрал вариант (D).

Было бы просто сделать динамическое количество уровней приоритета, чтобы оно устанавливалось при создании класса. Но делать это полностью динамически динамическим с приоритетами, так как любое int нецелесообразно с таким подходом, поскольку будет создано очень много неиспользуемых объектов BlockingCollection.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...