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

Joa*_*org 10 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
Run Code Online (Sandbox Code Playgroud)

Eli*_*ght 6

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

while True:
    sleep(1)
Run Code Online (Sandbox Code Playgroud)

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

更复杂的方式是拥有一个全局stopped 事件:

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)
Run Code Online (Sandbox Code Playgroud)

然后你的主循环可以将stopped事件设置为何False时获得KeyboardInterrupt

try:
    while not stopped.is_set():
        stopped.wait(1)
except KeyboardInterrupt:
    stopped.set()
Run Code Online (Sandbox Code Playgroud)

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

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

from signal import signal, SIGINT
def stop(signum, frame):
    stopped.set()
signal(SIGINT, stop)
Run Code Online (Sandbox Code Playgroud)

这使您可以定义在按Ctrl + C时发生的情况,而不会影响主循环的中间位置.所以你可以继续做q.join()而不用担心被Ctrl + C打断.当然,在上面的示例中,您不需要加入,但您可能还有其他原因要这样做.