Python多线程与队列场景下threadCounter>4时线程无法停止问题咨询
问题根本原因
该异常是任务供给总量和线程总消费能力不匹配导致的阻塞,核心逻辑问题如下:
- 每个工作线程固定执行3次循环,每次从队列取1个任务处理,只有完成3次消费后才会正常退出
- 当
threadCounter=4时,所有线程总消费能力为4*3=12个任务,刚好和主进程塞入队列的12个任务(range(1,13)共生成12个元素)完全匹配,所有线程都能完成全部消费逻辑正常退出 - 当
threadCounter>4时,比如示例中的6,总消费能力为6*3=18个任务,但队列仅放入了12个任务;任务被取空后,未完成消费次数的线程会直接阻塞在fifo_queue.get()调用上:queue.get()默认为永久阻塞模式,队列无元素时会一直等待新元素写入,不会主动返回 - 你设置的daemon线程仅会在主进程完全退出时被强制销毁,但主进程在
fifo_queue.join()返回后会执行os.system('pause')停驻,不会立刻退出,因此阻塞的工作线程会一直挂在后台,表现为无法正常停止
调试定位手段
可以通过以下方法快速定位根因:
- 打印队列实时状态:在
get()调用前后打印fifo_queue.qsize()的值,可快速发现队列空了之后仍有线程在尝试拉取任务 - 给
get()增加超时逻辑:将item = fifo_queue.get()改为item = fifo_queue.get(timeout=1),阻塞超过1秒就会抛出queue.Empty异常,报错栈会直接定位到阻塞的代码位置 - 补充线程执行日志:在循环入口、
get()执行前后、循环退出位置都增加打印日志,可清晰看到哪些线程未走完循环、卡在了哪一步 - 查看线程实时调用栈:用Pycharm、VSCode的调试功能,或者运行时用
py-spy等工具挂载到进程,可直接看到所有存活线程的当前调用栈,一眼就能识别出线程是阻塞在get()方法上
内容的提问来源于stack exchange,提问作者Eric Lo
相关产品推荐
相关产品推荐

