PFX ConcurrentQueue - 有没有办法从队列中删除特定项目

时间:2009-03-25 23:37:13

标签: c# .net concurrency pfx

我有一个应用程序,其ConcurrentQueue项目具有ID属性和每个项目的ConcurrentQueue任务,队列项目如下所示:

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

并且队列本身看起来像:

ConcurrentQueue<QueueItem> itemQueue;

我有一个线程在itemQueue上做一个foreach,从每个队列中取出一个项目并对其进行处理:

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中没有更多workItem时,我的问题就出现了 - 我想从itemQueue中删除那个queueItem - 就像......

  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 个答案:

答案 0 :(得分:4)

通常,没有有效的方法可以从队列中删除特定项目。它们通常具有O(1)队列并且已经出列,但O(n)将被删除,这是您的实现所做的。

一种替代结构叫做LinkedHashMap。如果您有兴趣,请查看Java implementation

它本质上是一个哈希表一个链表,允许O(1)队列,出队和删除。

这还没有在.Net中实现,但是网上有一些实现。

现在,问题是,为什么itemQueue是一个队列?从您的代码示例中,您永远不会从中排队或出列任何内容(除了导航删除问题)。我怀疑如果使用更合适的数据结构可以简化您的问题。您能举例说明其他代码访问itemQueue吗?

答案 1 :(得分:3)

这可能不适合所有人,但以下是我提出的从并发队列中删除项目的解决方案,因为这是第一个谷歌结果,我想我会留下我的解决方案。

我所做的是暂时将工作队列替换为空,将原始文件转换为列表并删除项目,然后从修改后的列表中创建一个新队列并将其放回。

在代码中(抱歉这是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

旁白:这是我的第一个答案,所以请随意提出一些编辑建议和/或编辑我的答案。

答案 2 :(得分:0)

队列是指您希望处理FIFO样式的项目,即LIFO的堆栈。还有一个concurrentdictionary和concurrentbag。确保队列实际上是您想要的。我不认为我会在同时发生的事情上做过预告。

您可能需要的是工作项的单个队列(让它们使用公共接口并在接口上创建队列,接口应该公开继承的类型,如果需要,可以在以后重新创建它)。如果工作项属于父项,则可以使用一个属性,该属性将保存父项的键(考虑键的GUID),并且父项可以保存在并发字典中,并根据需要引用/删除。

如果您必须按照自己的方式进行,请考虑添加标记。然后,您可以将项目队列中的项目标记为“已关闭”或其他任何内容,以便在它出列时将被忽略。