考虑下面我一直在学习多线程的例子。这只是 Python 3.5 队列文档的一个扩展示例。
它通过 4 个线程打印一些数字,在队列中生成一个错误,重试此元素,如果发生 KeyboardInterrupt 异常,应打印剩余的队列。
import threading
import queue
import time
import random
import traceback
def worker(q, active):
while True:
worker_item = q.get()
#if worker_item == None:
if not active.is_set():
break
time.sleep(random.random())
with threading.Lock():
if worker_item == 5 or worker_item == '5':
try:
print(threading.current_thread().name + ': ' + worker_item + ' | remaining queue: ' + str(list(q.queue)))
except TypeError:
print(threading.current_thread().name + ': ')
print(traceback.format_exc())
q.put(str(worker_item))
else:
print(threading.current_thread().name + ': ' + str(worker_item) + ' | remaining queue: ' + str(list(q.queue)))
q.task_done()
def main():
# INITIALIZE
num_threads = 4
stack1 = list(range(1, 21))
stack2 = list(range(101, 121))
q = queue.Queue()
active = threading.Event()
active.set()
# START THREADS
threads = []
for _ in range(num_threads):
t = threading.Thread(target=worker, args=(q, active))
t.start()
threads.append(t)
try:
# PUT STACK ITEMS ON QUEUE AND BLOCK UNTIL ALL TASKS ARE DONE
for stack1_item in stack1:
q.put(stack1_item)
q.join()
for stack2_item in stack2:
q.put(stack2_item)
q.join()
# STOP WORKER LOOP IN EVERY THREAD
#for _ in threads:
#q.put(None)
active.clear()
# WAIT UNTIL ALL THREADS TERMINATE
for t in threads:
t.join()
except KeyboardInterrupt:
print(traceback.format_exc())
print('remaining queue: ' + str(list(q.queue)))
#for _ in threads:
#q.put(None)
active.clear()
for t in threads:
t.join()
if __name__ == '__main__':
main()
如果我按原样运行脚本(没有键盘中断(,它不会终止。我必须杀死信号。 但是如果我评论/取消评论以下行(不使用事件并以文档方式进行......
comment / worker / if not active.is_set():
uncomment / worker / #if worker_item == None:
comment / main / active.clear()
uncomment / main / #for _ in threads:
#q.put(None)
comment / main / except / active.clear()
uncomment / main / except / #for _ in threads:
#q.put(None)
它确实以退出代码 0 退出。为什么?
为什么需要将 Nones 放入队列?如果不将 Nones 放入队列,解决方案是什么?
有两种类型的线程:守护程序和非守护进程。默认情况下,所有线程都是非守护程序。只要至少有一个非守护程序线程,进程就会保持活动状态。
这意味着要停止该过程,您必须:
- 停止它的所有线程(这是你注释掉的代码通过使用
None
将工作线程踢出无限等待q.get()
所做的(;或者
使 worker 守护进程线程,在这种情况下, - 进程将在主线程停止后立即停止(如果要确保 worker 已完成其任务,这将需要格外小心(。