代码干货 | 一个Reentrant Error引发的对Python信号机制的探索和思考

本文来源于阿里云-云栖社区,原文点击这里


前几天工作时遇到了一个匪夷所思的问题。经过几次尝试后问题得以解决,但问题产生的原因却仍令人费解。查找 SO 无果,我决定翻看 Python 的源码。断断续续地研究了几天,终于恍然大悟。撰此文以记。

本文环境:

  • Ubuntu 16.04 (64 bit)
  • Python 3.6.2

使用的 C 源码可以从 Python 官网 获取。

起因

工作时用到了 celery 作为异步任务队列,为方便调试,我写了一个脚本用以启动/关闭 celery 主进程。代码简化后如下:


  1. import sys 
  2.  
  3. import subprocess 
  4.  
  5. # ... 
  6.  
  7. celery_process = subprocess.Popen( 
  8.  
  9.     ['celery''-A''XXX''worker'], 
  10.  
  11.     stdout=subprocess.PIPE, 
  12.  
  13.     stderr=sys.stderr 
  14.  
  15.  
  16. try: 
  17.  
  18.     # Start and wait for server process 
  19.  
  20. except KeyboardInterrupt: 
  21.  
  22.     # Ctrl + C pressed 
  23.  
  24.     celery_process.terminate() 
  25.  
  26.     celery_process.wait()  


代码启动了 celery worker,并尝试在捕获到 KeyboardInterrupt 异常时将其热关闭。

初看上去没什么问题。然而实际测试时却发生了十分诡异的事情:按下 Ctrl+C 后,程序 偶尔 会抛出这样的异常:RuntimeError: reentrant call inside <_io.BufferedWriter name='<stdout>’>。诡异之处有两点:

异常发生的时机有随机性

异常的 traceback 指向 celery 包,也就是说这是在 celery 主进程内部发生的异常

这个结果大大出乎了我的意料。随机性异常是众多最难缠的问题之一,因为这常常意味着并发问题,涉及底层知识,病灶隐蔽,调试难度大,同时没有有效的手段判断问题是否彻底解决(可能只是降低了频率)。


 展开全文

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章