PFX ConcurrentQueue - есть ли способ удалить конкретный элемент из очереди - PullRequest
1 голос
/ 26 марта 2009

У меня есть приложение, у которого есть ConcurrentQueue элементов, у которых есть свойство ID и ConcurrentQueue задач для каждого элемента, элементы очереди выглядят так:

class QueueItem {
  public int ID { get; set; }
  public ConcurrentQueue<WorkItem> workItemQueue { get; set; }
}

и сама очередь выглядит так:

ConcurrentQueue<QueueItem> itemQueue;

У меня есть один поток, выполняющий foreach над itemQueue, выписывающий элемент из каждой очереди и работающий над ним:

foreach(var queueItem in itemQueue) {
  WorkItem workItem;
  if (queueItem.workItemQueue.TryDequeue(out workItem))
    doWork(workItem);
  else
    // no more workItems for this queueItem
}

Я использую ConcurrentQueues, потому что у меня есть отдельный поток, потенциально добавляющий queueItems к itemQueue и добавляющий workItems к каждому workItemQueue.

Моя проблема возникает, когда у меня больше нет рабочих элементов в queueItem - я хотел бы удалить этот queueItem из itemQueue - что-то вроде ...

  if (queueItem.workItemQueue.TryDequeue(out workItem))
    doWork(workItem);
  else
    itemQueue.TryRemove(queueItem);

... но я не могу найти способ сделать это легко. Я пришел к выводу, что нужно удалить каждый QueueItem из очереди, а затем поставить его в очередь, если в workItemQueue все еще есть WorkItems:

for (int i = 0; i < itemQueue.Count; i++) {
  QueueItem item;
  itemQueue.TryDequeue(out queueItem);
  if (queueItem.workItemQueue.TryDequeue(out workItem)) {
    itemQueue.Enqueue(queueItem);
    doWork(workItem);
  }
  else
    break;
}

Есть ли лучший способ выполнить то, что я хочу, с помощью PFX ConcurrentQueue, или это разумный способ сделать это, если я использую пользовательскую реализацию очереди / списка или я что-то упустил?

Ответы [ 3 ]

4 голосов
/ 15 июня 2009

Как правило, не существует эффективных способов удаления определенных элементов из очередей. Обычно они имеют O (1) очередь и очереди, но O (n) удаляет, что и делает ваша реализация.

Одной из альтернативных структур является LinkedHashMap. Посмотрите на реализацию Java , если вы заинтересованы.

По сути, это хэш-таблица и связанный список, который позволяет O (1) очереди, удаления из очереди и удаления.

Это еще не реализовано в .Net, но в Интернете есть несколько реализаций.

Теперь вопрос в том, почему itemQueue - очередь? Исходя из примеров кода, вы никогда не ставите в очередь или исключаете из него что-либо (кроме навигации по проблеме удаления). У меня есть подозрение, что ваша проблема может быть упрощена, если использовать более подходящую структуру данных. Не могли бы вы привести примеры того, какие другие части кода имеют доступ к itemQueue?

3 голосов
/ 23 августа 2013

Это может не сработать для всех, но вот решение, которое я придумала для удаления элемента из параллельной очереди, так как это первый результат Google, я решил оставить свое решение позади.

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

В коде (извините, это VB.net, а не C #):

Dim found As Boolean = False
//'Steal the queue for a second, wrap the rest in a try-finally block to make sure we give it back
Dim theCommandQueue = Interlocked.Exchange(_commandQueue, New ConcurrentQueue(Of Command))
Try
    Dim cmdList = theCommandQueue.ToList()
    For Each item In cmdList
        If item Is whateverYouAreLookingFor Then
            cmdList.Remove(item)
            found = True
        End If
    Next
    //'If we found the item(s) we were looking for, create a new queue from the modified list.
    If found Then
        theCommandQueue = New ConcurrentQueue(Of Command)(cmdList)
    End If
Finally
    //'always put the queue back where we found it
    Interlocked.Exchange(_commandQueue, theCommandQueue)
End Try

В сторону: это мой первый ответ, так что не стесняйтесь добавить несколько советов по редактированию и / или отредактируйте мой ответ.

0 голосов
/ 07 августа 2014

Очереди предназначены для обработки элементов в стиле FIFO, стеки для LIFO. Существует также concurrentdictionary и concurrentbag. Убедитесь, что очередь на самом деле то, что вы хотите. Я не думаю, что когда-либо делал бы foreach на параллельном очереди.

То, что вы, вероятно, хотите, - это отдельная очередь для ваших рабочих элементов (если они используют общий интерфейс и создают очередь на интерфейсе, интерфейс должен предоставлять унаследованный тип, к которому он может быть преобразован позже, если это необходимо). Если рабочие элементы принадлежат родительскому элементу, то можно использовать свойство, которое будет содержать ключ к родительскому элементу (рассмотрим GUID для ключа), а родительский элемент можно сохранить в параллельном и ссылаться на него / удалять по мере необходимости.

Если вы должны сделать это так, как хотите, подумайте о добавлении флага. затем вы можете пометить элемент в очереди элементов как «закрытый» или что-то еще, так что при снятии с него его игнорируют.

...