本文来源于阿里云-云栖社区,原文点击这里。
前几天工作时遇到了一个匪夷所思的问题。经过几次尝试后问题得以解决,但问题产生的原因却仍令人费解。查找 SO 无果,我决定翻看 Python 的源码。断断续续地研究了几天,终于恍然大悟。撰此文以记。
本文环境:
- Ubuntu 16.04 (64 bit)
- Python 3.6.2
使用的 C 源码可以从 Python 官网 获取。
起因
工作时用到了 celery 作为异步任务队列,为方便调试,我写了一个脚本用以启动/关闭 celery 主进程。代码简化后如下:
- import sys
- import subprocess
- # ...
- celery_process = subprocess.Popen(
- ['celery', '-A', 'XXX', 'worker'],
- stdout=subprocess.PIPE,
- stderr=sys.stderr
- )
- try:
- # Start and wait for server process
- except KeyboardInterrupt:
- # Ctrl + C pressed
- celery_process.terminate()
- celery_process.wait()
代码启动了 celery worker,并尝试在捕获到 KeyboardInterrupt 异常时将其热关闭。
初看上去没什么问题。然而实际测试时却发生了十分诡异的事情:按下 Ctrl+C 后,程序 偶尔 会抛出这样的异常:RuntimeError: reentrant call inside <_io.BufferedWriter name='<stdout>’>。诡异之处有两点:
异常发生的时机有随机性
异常的 traceback 指向 celery 包,也就是说这是在 celery 主进程内部发生的异常
这个结果大大出乎了我的意料。随机性异常是众多最难缠的问题之一,因为这常常意味着并发问题,涉及底层知识,病灶隐蔽,调试难度大,同时没有有效的手段判断问题是否彻底解决(可能只是降低了频率)。