我正在学习使用Queue模块,并且对于如何使队列使用者线程知道队列已完成感到有点困惑。理想情况下,我想在使用者线程中使用get()
,并在队列标记为“完成”时抛出异常。有没有比通过附加标记值来标记队列中的最后一项更好的方式进行沟通?
答案 0 :(得分:8)
队列本身并不具备完成或完成的想法。它们可以无限期使用。要在完成后将其关闭,您确实需要在结尾处放置None或其他一些魔术值并编写逻辑来检查它,如您所述。理想的方法可能是对Queue对象进行子类化。
请参阅http://en.wikipedia.org/wiki/Queue_(data_structure)以了解有关队列的更多信息。
答案 1 :(得分:8)
根据suggestions和其他人的Glenn Maynard(谢谢!)的某些内容,我决定汇总实现Queue.Queue
方法的close
后代。它以原始(未打包)module的形式提供。当我有更多的时间时,我会清理一下并正确打包。目前,该模块仅包含CloseableQueue
类和Closed
异常类。我打算将其扩展为包含Queue.LifoQueue
和Queue.PriorityQueue
的子类。
目前处于一个非常初步的状态,也就是说虽然它通过了它的测试套件,但我还没有真正用它做任何事情。你的旅费可能会改变。我将通过令人兴奋的消息更新这个答案。
CloseableQueue
类与Glenn的建议略有不同,因为关闭队列将阻止将来put
,但在队列清空之前不会阻止将来get
。这对我来说最有意义;似乎清除队列的功能可以作为单独的mixin *添加,该功能与可关性功能正交。所以基本上使用CloseableQueue
,通过关闭队列,您可以指示最后一个元素是put
。还有一个选项可以通过将last=True
传递给最终put
调用来原子地执行此操作。在清空队列后,对put
的后续调用以及后续调用get
以及与这些描述匹配的未完成阻止调用将引发Closed
异常。
这对于单个生产者为一个或多个消费者生成数据的情况非常有用,但对于消费者正在等待特定项目或项目集的多重布置也很有用。特别是它没有提供确定所有生产者都已完成生产的方法。实现这一目标需要提供一些注册生产者的方法(.open()
?),以及表明生产者注册本身已经关闭的方法。
非常欢迎建议和/或代码审查。我没有编写大量的并发代码,但希望测试套件足够彻底,以至于代码传递它的事实表明了代码的质量,而不是套件的缺乏。我能够重用Queue模块测试套件中的一堆代码:文件本身包含在此模块中,并用作各种子类和例程的基础,包括回归测试。这可能(希望)有助于避免测试部门完全无能为力。代码本身只会以相当小的更改覆盖Queue.get
和Queue.put
,并添加close
和closed
方法。
我有意避免在代码本身和测试套件中使用像上下文管理器这样的任何新奇的功能,以保持代码向后兼容,就像Queue模块本身一样确实倒退了。我可能会在某些时候添加__enter__
和__exit__
方法;否则,contextlib的closing函数应该适用于CloseableQueue实例。
*:这里我松散地使用术语“mixin”。由于Queue
模块的类是旧式的,因此需要使用类工厂函数混合mixins;一些限制适用;在Guido禁止的地方提供无效。
CloseableQueue模块现在提供CloseableLifoQueue
和CloseablePriorityQueue
个类。我还添加了一些便利函数来支持迭代。还需要将其作为一个合适的包装进行修改。有一个类工厂函数,可以方便地对其他Queue.Queue
派生类进行子类化。
CloseableQueue
现已通过PyPI提供,例如与
$ easy_install CloseableQueue
欢迎提出评论和批评,特别是来自这个答案的匿名downvoter。
答案 2 :(得分:5)
哨兵是一种关闭队列的自然方式,但有几件事需要注意。
首先,请记住,您可能有多个消费者,因此您需要为每个正在运行的消费者发送一次哨兵,并保证每个消费者只消耗一个哨兵,以确保每个消费者都收到其关闭的哨兵。 / p>
其次,请记住Queue定义了一个接口,并且在可能的情况下,代码应该与底层Queue无关。您可能有一个PriorityQueue,或者您可能有一些其他类暴露相同的接口并以其他顺序返回值。
不幸的是,很难处理这两个问题。为了处理不同队列的一般情况,正在关闭的消费者必须在收到其关闭的sentinel之后继续使用值,直到队列为空。这意味着它可能消耗另一个线程的哨兵。这是Queue接口的一个弱点:它应该有一个Queue.shutdown
调用,导致所有消费者抛出异常,但是缺少这个异常。
所以,在实践中:
答案 3 :(得分:1)
队列是一个FIFO(先进先出)寄存器,所以请记住,消费者可以比生产者更快。当消费者线程检测到队列为空时,通常会实现以下操作之一:
如果您不希望消费者线程在作业完成后终止,而不是将Sentinel值放入队列以终止任务。
答案 4 :(得分:0)
这样做的最佳实践方法是让队列本身通知客户端它已达到“完成”状态。然后,客户可以采取任何适当的行动。
你的建议;检查队列以查看它是否定期完成,将是非常不受欢迎的。轮询是多线程编程中的反模式,您应该始终使用通知。
编辑:
所以你的说法是队列本身知道它已根据某些标准“完成”并需要通知客户这一事实。我认为你是正确的,最好的方法是在客户端调用get()并且队列处于完成状态时抛出。如果你抛出这个就会否定在客户端需要一个标记值。在内部,队列可以以任何方式检测到它是“完成的”,例如它队列是空的,它的状态设置为完成等我没有看到任何需要的哨兵值。