Python ThreadPool进程挂起问题求助(Python 2.7.14)
老哥,这种进程莫名挂起但抓不到错误的情况确实头疼,尤其是Python 2.7这种老版本,有些坑容易被忽略。结合我踩过的坑,给你列几个排查方向,你可以挨个试试:
排查方向
1. 检查无超时的阻塞IO操作
Python 2.7里很多默认的同步IO调用是没有超时设置的,比如urllib2.urlopen()、原生socket调用,如果对方服务无响应、网络卡顿,进程会一直卡在IO上,这种情况不属于Python的“异常”,所以try-except根本抓不到。
- 给所有IO操作手动加超时:比如
urllib2.urlopen(url, timeout=10) - 全局设置socket超时:
import socket; socket.setdefaulttimeout(10)
2. 排查多线程/多进程死锁
哪怕逻辑简单,只要用到了多线程/多进程,就可能出现死锁:比如两个线程互相等待对方释放锁,或者threading.Lock在异常分支没正确解锁。
- 挂起时用
threading.enumerate()打印所有线程的状态,看有没有线程卡在waiting状态 - 用
gdbattach到进程,执行py-bt命令查看每个线程的Python调用栈(Linux环境)
3. 检查第三方C扩展的问题
如果你的脚本用到了C语言写的第三方库(比如某些加密、数据处理库),C层的阻塞或崩溃不会被Python的异常捕获,甚至直接让进程“僵死”。
- 用
strace(Linux)跟踪进程的系统调用,看它卡在哪个系统调用上(比如read/write某个文件描述符,或者sem_wait同步调用) - 暂时禁用C扩展相关代码,看是否还会挂起
4. 标准输出/输出阻塞
如果脚本有大量打印,且输出被重定向到文件/管道,当缓冲区满了之后,进程会阻塞在print或sys.stdout.write上,这种情况也不会触发异常。
- 试试强制刷新缓冲区:
print(xxx); sys.stdout.flush() - 暂时关闭输出(比如把
sys.stdout重定向到/dev/null),看是否还会挂起
5. 用工具抓挂起现场
当进程挂起时别直接杀掉,用工具留个现场:
- 启用
faulthandler(Python 2.7.8+支持):
然后给挂起的进程发送import faulthandler faulthandler.enable()SIGUSR1信号,它会把所有线程的调用栈打印到标准错误输出,帮你定位卡在哪一行。
先从IO超时和死锁这两个最常见的方向入手,大概率能找到问题。
内容的提问来源于stack exchange,提问作者DesperateCODER
相关产品推荐
相关产品推荐

