使用线程和队列时如何处理异常?

时间:2009-05-04 13:31:47

标签: python multithreading exception

如果我有一个使用线程和队列的程序,我如何获得停止执行的异常?这是一个示例程序,使用ctrl-c无法停止(基本上是从python文档中删除的)。

from threading import Thread
from Queue import Queue
from time import sleep

def do_work(item):
    sleep(0.5)
    print "working" , item

def worker():
        while True:
            item = q.get()
            do_work(item)
            q.task_done()

q = Queue()

num_worker_threads = 10

for i in range(num_worker_threads):
     t = Thread(target=worker)
    # t.setDaemon(True)
     t.start()

for item in range(1, 10000):
    q.put(item)

q.join()       # block until all tasks are done

1 个答案:

答案 0 :(得分:6)

最简单的方法是将所有工作线程作为守护程序线程启动,然后让主循环为

while True:
    sleep(1)

按Ctrl + C将在主线程中引发异常,并且当解释器退出时,所有守护程序线程都将退出。这假设您不希望在退出之前在所有这些线程中执行清理。

更复杂的方法是拥有全球stopped Event

stopped = Event()
def worker():
    while not stopped.is_set():
        try:
            item = q.get_nowait()
            do_work(item)
        except Empty:      # import the Empty exception from the Queue module
            stopped.wait(1)

然后,您的主循环可以将stopped事件设置为False <{1}}

KeyboardInterrupt

这使您的工作线程可以完成他们想要的操作而不是让每个工作线程都成为守护进程并在执行过程中退出。你也可以做任何你需要的清理工作。

请注意,此示例未使用try: while not stopped.is_set(): stopped.wait(1) except KeyboardInterrupt: stopped.set() - 这会使事情变得更复杂,尽管您仍然可以使用它。如果你这样做,那么最好的办法是使用信号处理程序而不是异常来检测q.join()。例如:

KeyboardInterrupt

这使您可以定义在按Ctrl + C时发生的情况,而不会影响主循环在中间的任何内容。所以你可以继续from signal import signal, SIGINT def stop(signum, frame): stopped.set() signal(SIGINT, stop) 而不用担心被Ctrl + C打断。当然,在上面的示例中,您不需要加入,但您可能还有其他原因。