【问题标题】:How do I add an item to the front of the queue?如何将项目添加到队列的前面?
【发布时间】:2018-05-11 01:34:29
【问题描述】:

我正在创建一个 Windows 服务,它使用 FileSystemWatcher 来监视特定文件夹以添加特定文件类型。由于 Created 事件和文件实际准备好被操作之间的差距,我创建了一个 Queue<T> 来保存需要处理的文件名。在 Created 事件处理程序中,项目被添加到队列中。然后使用计时器,我定期从队列中抓取第一个项目并处理它。如果处理失败,则将该项目添加回队列,以便服务稍后重试处理。

这很好用,但我发现它有一个副作用:在重试所有旧的重试项目之前,不会发生对新项目的第一次处理尝试。由于队列可能包含许多项目,我想将新项目强制到队列的前面,以便首先处理它们。但是从Queue<T> documentation来看,没有明显的方法可以将一个项目添加到队列的前面。

我想我可以为新项目创建第二个队列并优先处理那个队列,但只有一个队列似乎更简单。

那么有没有一种简单的方法可以将项目添加到队列的前面?

【问题讨论】:

  • 如果您要添加到队列的前端和尾部,从迂腐的角度来说,它不再是队列。
  • 为什么不将重试项添加到自己的集合中以供以后处理?
  • 在 STL 中,您将使用双端队列(我听说它读作“deck”,但我真的不知道)。这是一个双端队列。 cplusplus.com/reference/stl/deque System.Collections.Generic 中似乎没有,但您可以按照您描述的方式编写您自己的。

标签: c#


【解决方案1】:

看起来你想要一个LinkedList<T>,它允许你做AddFirst()AddLast()RemoveFirst()RemoveLast()之类的事情。

【讨论】:

  • +1 这实际上是一个很好的解决方案。哎呀,我怎么没想到呢!
  • +++ 确实是一个很棒的解决方案。这就是我一直在寻找的;一个实际上不是队列的队列。 :)
  • 另外,LinkedList 是双向链接的,所以上面列出的所有操作都是 O(1)。
  • 所以你在这里实际上是两个堆栈。如果您不需要先进先出顺序,这很好。
  • 这是最好的答案,因为它允许您添加到开头或结尾并从开头或结尾删除。
【解决方案2】:

只需在计时器回调中使用 Peek 方法而不是 Dequeue。 如果处理成功,则将该项目出列。

【讨论】:

    【解决方案3】:

    好吧,我同意 CanSpice 的观点;但是,您可以:

    var items = queue.ToArray();
    queue.Clear();
    queue.Enqueue(newFirstItem);
    foreach(var item in items)
        queue.Enqueue(item);
    

    讨厌的 hack,但它会起作用 ;)

    您可能会考虑添加第二个队列实例。这是您首先检查/执行的“优先”队列。这样会干净一些。你甚至可以创建自己的队列类来把它包装得漂亮整洁,就像;)

    【讨论】:

    • 我确实想到清空队列并重新填充它,但想象的机制比您建议的要丑得多。但是,自定义队列类很有趣。
    • 你可以添加一个扩展方法让它更漂亮
    【解决方案4】:

    在处理之前不要从队列中取出,这是一种替代解决方案。

    使用queue.Peek() 获取您的第一个项目,只有在操作成功时使用Dequeue()

    Peek() 之前使用queue.Count > 0,否则会得到“队列为空”的结果。

    【讨论】:

      【解决方案5】:

      我建议使用两个队列:一个用于新项目,一个用于重试项目。将两个队列包装在一个对象中,该对象与队列具有相同的语义,就删除而言,但允许您将事物标记为进入新队列或插入时重试队列。比如:

      public class DoubleQueue<T>
      {
          private Queue<T> NewItems = new Queue<T>();
          private Queue<T> RetryItems = new Queue<T>();
      
          public Enqueue(T item, bool isNew)
          {
              if (isNew)
                  NewItems.Enqueue(item);
              else
                  RetryItems.Enqueue(item);
          }
      
          public T Dequeue()
          {
              if (NewItems.Count > 0)
                  return NewItems.Dequeue();
              else
                  return RetryItems.Dequeue();
          }
      }
      

      当然,您需要有一个 Count 属性来返回两个队列中的项目数。

      如果您有两种以上类型的项目,那么是时候升级到优先队列了。

      【讨论】:

      • 这毫无意义。队列就是队列。他应该使用链表。
      • 链表方法的问题在于,他真的不应该将所有新项目都放在前面,尽管他确实要求这样做。他希望在需要重试的项目之前处理新项目。他不希望在旧的新项目之前处理新项目。因此,正确的解决方案是两个队列,这样包装得体。
      【解决方案6】:

      听起来你想要的是Stack - 这是一个 LIFO(后进先出)缓冲区。

      【讨论】:

      • -1,他只想将某些类型的项目插入队列的开头,而不是全部。因此,堆栈对于他的目的来说同样是有缺陷的。
      • @csharptest.net,我已经三次阅读了 OP 的问题,但这不是它扫描给我的方式。在我看来,OP 总是希望在处理旧项目之前处理新项目。
      • @Kirk 我原本也是这么想的,但重读后似乎他想让所有新项目优先于旧项目(不管旧项目等待了多长时间)跨度>
      【解决方案7】:

      您需要一个优先队列。看看C5 Collections Library。它的 IntervalHeap 实现了 IPriorityQueue 接口。 C5 集合库也很不错。

      我相信您也可以在 http://www.codeproject.comhttp://www.codeplex.com 找到实现。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-12-18
        • 2019-04-11
        • 1970-01-01
        • 1970-01-01
        • 2015-08-23
        • 1970-01-01
        • 2017-08-08
        相关资源
        最近更新 更多