清空队列中的所有项目

80

我怎样可以清空一个队列。比如说,我当前有一些数据在队列里,但是由于某种原因我不需要这些数据了,只想要清空队列。

有什么方法吗?这个行得通吗:

oldQueue = Queue.Queue()

4
如果你阅读文档,它会指引你查看队列模块的最新源代码,在那里你可以看到FIFO队列的内部表示是一个deque。在deque的文档中,你会注意到它有一个clear()方法,这正是你想要的。你可能还想将该调用包装在q.mutex.acquire()q.mutex.release()中,就像代码中对这类操作的其余部分一样,以使其线程安全。 - martineau
4个回答

129
q = Queue.Queue()
q.queue.clear()

编辑 为了清晰简洁,我忽略了线程安全的问题,但@Dan D是正确的,以下内容更好。

q = Queue.Queue()
with q.mutex:
    q.queue.clear()

23
如果你使用 with q.mutex: q.queue.clear() 这个操作,它将是线程安全的。 - Dan D.
8
请纠正我如果我错了,但这实际上对我来说似乎是一个糟糕的想法。. mutex. queue 似乎没有文档,如果我没有弄错的话,在其块内部执行任何类似于甚至是q.get_nowait的操作会导致q.mutex 死锁 - n611x007
3
n611x007和user2357112是正确的。我希望在未记录/私有成员之前加上下划线,否则会让我误认为clear()是安全API的一部分。清除队列似乎并不是一个好主意,最好的方法是读取队列并删除不必要的项目。 - Jean-Bernard Jansen
2
请注意,正如V.E.O的答案中所述,仅清除队列是不够的,因为即使队列为空,相应的任务仍未标记为已完成。这可能会导致您的代码被锁定。 - Chrigi
3
我也使用了下面这种方法:使用q.mutex锁定: size = len(q.queue) q.queue.clear() q.unfinished_tasks -= size这解决了join()阻塞的问题。 - shipperizer
显示剩余3条评论

48

你无法清空队列,因为每次放入元素都会增加 unfinished_tasks 的成员。 join 方法依赖于该值。 并且还需要通知 all_tasks_done。

with q.mutex:
    q.queue.clear()
    q.all_tasks_done.notify_all()
    q.unfinished_tasks = 0

或者以合适的方式,使用get和task_done对以安全方式清除任务。
    while not q.empty():
        try:
            q.get(block=False)
        except Empty:
            continue
        q.task_done()

或者创建一个新的队列并删除旧的队列。


对于那个方法也要小心使用。文档中写道:"如果empty()返回False,并不能保证后续调用get()不会阻塞"。 - Lack
@Lack 是的,通过非阻塞获取来修复它。 - V.E.O
3
第二种方式("体面的"方式)似乎是最安全和更优雅的方式,并且仅使用文档记录的公共API。有人能否确认一下? - MestreLion
看起来这两个在并发放置时会有稍微不同的行为。第一个会阻塞并发放置,而第二个会在下一次循环中捕获并移除并发放置。对吗? - Michael Delgado
优秀的方式并不保证它会结束。更不用说循环开销了。我会坚持第一种方式。 - freakish
@V.E.O,你救了我很多麻烦。如果队列使用join(),则接受的解决方案无效。未完成任务计数不会重置。 - Gerzson

12

对我来说,这似乎做得很好。如果我漏掉了什么重要的东西,欢迎发表评论/补充。

class Queue(queue.Queue):
  '''
  A custom queue subclass that provides a :meth:`clear` method.
  '''

  def clear(self):
    '''
    Clears all items from the queue.
    '''

    with self.mutex:
      unfinished = self.unfinished_tasks - len(self.queue)
      if unfinished <= 0:
        if unfinished < 0:
          raise ValueError('task_done() called too many times')
        self.all_tasks_done.notify_all()
      self.unfinished_tasks = unfinished
      self.queue.clear()
      self.not_full.notify_all()

2
这似乎是最有效(调用.clear())和正确(包括当前处理项的通知)的方法。 - freakish
一个多进程实现会像这样吗? - Mat90

0
如果你只想要一个空队列,并且不关心旧队列的垃圾回收,只需使用相同的句柄实例化一个新的队列。例如,
    q = Queue(maxsize=max_size)
    q.put(1)
    q.put(2)
    q.put(3)
    q.put(4)

现在为了清空这个队列,你只需要简单地写下以下内容。
    q = Queue(maxsize=max_size)

此时,您应该有一个空队列。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接