2

我有一个应用程序,它有一个项目的 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 中没有更多 workItems 时,我的问题就出现了 - 我想从 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 完成我想要的,或者这是一种合理的方法,我应该使用自定义并发队列/列表实现还是我错过了什么?

4

3 回答 3

4

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

一种替代结构是称为 LinkedHashMap 的结构。如果您有兴趣,请查看Java 实现。

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

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

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

于 2009-06-15T08:27:26.147 回答
4

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

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

在代码中(对不起,这是 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

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

于 2013-08-23T15:14:26.430 回答
0

当您想要以 FIFO 样式处理项目时,队列意味着 LIFO 堆栈。还有一个concurrentdictionary和一个concurrentbag。确保队列实际上是您想要的。我认为我永远不会在并发队列上进行 foreach 。

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

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

于 2014-08-06T20:28:40.983 回答