代码之家  ›  专栏  ›  技术社区  ›  Whisk

pfx concurrentqueue-是否可以从队列中删除特定项

  •  1
  • Whisk  · 技术社区  · 15 年前

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

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

    队列本身看起来像:

    ConcurrentQueue<QueueItem> itemQueue;
    

    我有一个线程在item queue上执行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。

    当我在一个队列项中没有更多的工作项时,我的问题出现了-我想从该队列项中删除该队列项-类似于…

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

    …但我找不到一个简单的方法。我提出的方法是将每个队列项从队列中取出,然后在WorkitemQueue中仍有Workitem的情况下将其排队:

    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 concurrent queue来完成我想要的工作,或者这是一种合理的方法,我应该使用自定义的并发队列/列表实现,还是缺少一些东西?

    3 回复  |  直到 10 年前
        1
  •  4
  •   biozinc    15 年前

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

    另一种结构称为Linkedhashmap。看看 Java implementation 如果你感兴趣的话。

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

    这还没有在.NET中实现,但是有一些实现在Web上浮动。

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

        2
  •  3
  •   DjScribbles    11 年前

    这可能不适用于所有人,但下面是我为从并发队列中删除项目而提出的解决方案,因为这是第一个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
    

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

        3
  •  0
  •   VoteCoffee    10 年前

    队列是指当您想要处理FIFO样式的项目时,即后进先出的堆栈。还有一个同时发生的动作和一个同时发生的动作。确保队列实际上是您想要的。我不认为我会在同一个队列上做前臂。

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

    如果您必须按照您的方式来做,请考虑添加一个标志。然后,您可以将itemqueue中的项目标记为“已关闭”或其他任何项,这样当它出列时,它将被忽略。