Python:在引发异常并且未处理异常后,是否会杀死一个线程?

时间:2013-02-10 02:39:45

标签: python multithreading queue

官方文档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()方法将引发异常,这应该会终止该线程。这是对的吗?

2 个答案:

答案 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