【问题标题】:PFX ConcurrentQueue - is there a way to remove a specific item from the queuePFX ConcurrentQueue - 有没有办法从队列中删除特定项目
【发布时间】:2009-03-25 23:37:13
【问题描述】:

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

【问题讨论】:

  • 个人不知道。 PFX 有点新,但我很惊讶到目前为止没有人回答你的问题。

标签: c# .net concurrency pfx


【解决方案1】:

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

另一种结构称为 LinkedHashMap。如果您有兴趣,请查看Java implementation

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

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

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

【讨论】:

    【解决方案2】:

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

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

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

    除此之外:这是我的第一个答案,因此请随时提出一些编辑建议和/或编辑我的答案。

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-15
        • 2018-06-12
        • 1970-01-01
        相关资源
        最近更新 更多