官方文档here给出了以下示例:
def worker():
while True:
item = q.get()
do_work(item)
q.task_done()
q = Queue()
for i in range(num_worker_threads):
t = Thread(target=worker)
t.daemon = True
t.start()
for item in source():
q.put(item)
q.join() # block until all tasks are done
我想确保在主线程进行之前所有线程都被杀死。我想在处理完队列中的所有任务之后,q.get()方法将引发异常,这应该会终止该线程。这是对的吗?
答案 0 :(得分:2)
没有。如果队列中没有项目,则默认情况下get
将等待项目放入队列。如果您希望在没有其他商品时引发异常,请将其传递给block=False
或使用get_nowait
。
一旦你使用非阻塞get
,它应该都可以工作,但是由于正常情况下引发的异常,你的线程死亡是相当不优雅的。我建议使用try
块来包围它,如果由于队列为空而抛出异常,请干净地停止线程:
try:
item = q.get(block=False)
except queue.Empty:
return
答案 1 :(得分:0)
如果do_work()
的任何调用引发异常,则运行它的线程将退出。您的主要帖子将永久阻止 q.join()
,因为q.get()
在这种情况下未被q.task_done()
跟踪。
您可以使用线程池重写示例:
from multiprocessing.dummy import Pool # use threads
p = Pool(num_worker_threads)
for _ in p.imap_unordered(do_work, source()):
pass
p.close()
p.join() # no threads after this point
在这种情况下,如果do_work()
引发异常;它被传播到主线程并退出(池线程是守护进程,因此它们不会使程序保持运行)。
基于Queue
的解决方案的另一种替代方法是将sentinel值放入队列(每个线程一个值),如果遇到标记,则退出worker()
example:
STOP = object()
def worker(queue):
for item in iter(queue.get, STOP): # until STOP is encountered
do_work(item)
# instead of `q.join()`
for _ in threads: q.put(STOP)
for t in threads: t.join() # no threads after this point