作家
登录

一个Reentrant Error引发的对Python信号机制的探索和思考

作者: 来源: 2017-09-14 11:04:12 阅读 我要评论

51CTO诚邀您9月23号和秒拍/国美/美团元专家一路聊智能CDN的优化之路,抓紧时光哦!


写在前面

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

本文情况:

  • Ubuntu 16.04 (64 bit)
  • Python 3.6.2

应用的 C 源码可以大年夜 Python 官网 获取。

原由

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

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

初看上去没什愦问题。然而实际测试时却产生了十分诡异的工作:按下 Ctrl+C 后,法度榜样 有时 会抛出如许的异常:RuntimeError: reentrant call inside <_io.BufferedWriter name='<stdout>’>。诡异之处有两点:

这个结不雅大年夜大年夜出乎了我的料想。随机性异常是浩瀚最难缠的问题之一,因为这常平平易近味着并发问题,涉及底层常识,病灶隐蔽,调试难度大年夜,同时没有有效的手段断定问题是否彻调果断(可能只是降低了频率)。

异常信息中有两个词很关键:reentrant 和 stdout。reentrant call 解释有一个弗成重入的函数被递归调用了;stdout 则指清楚明了产生的地点和机会。初步可以剖断:因为某种原因,有两股控制流在同时操控 stdout。

“可重入”是什么?根据 Wikipedia 的定义:如不雅一个子法度榜样能在履行时被中断并在之后被精确地、安然地唤起,它就被称为可重入的。依附于全局数据的过程是弗成重入的,如 printf(依附于全局文件描述符)、malloc(依附与和堆相干的一系列数据构造)等函数。须要留意的是,可重人道(reentrant)与 线程安然性(thread-safe)并不等价,甚至不存在包含关系,Wikipedia 中给出了相干的反例。

多次测验测验后,出现了一条线索:有时刻 worker: Warm shutdown (MainProcess) 这个字符串会被二次打印,机会不肯定。这句话是 celery 将要热封闭时的提示语,二次出现只可能是主过程收到了第二个旌旗灯号。浏览 celery 的文档 可知,SIGINT 和 SIGTERM 旌旗灯号可以激发烧封闭。回头浏览我的代码,个中只有一处发送了 SIGTERM 旌旗灯号(celery_process.terminate()),至于另一个神秘的旌旗灯号,我困惑是 SIGINT。

SO 一下,结不雅印证了我的猜想:

If you are generating the SIGINT with Ctrl-C>

  • # ... 
  •  
  • try: 
  •  
  •     # Start and wait for server process 
  •  
  • except KeyboardInterrupt: 
  •  
  •     # Ctrl + C pressed 
  •  
  •     pass 
  •  
  • else
  •  
  •     # Signal SIGTERM if no exception raised 
  •  
  •     celery_process.terminate() 
  •  
  • finally: 
  •  
  •     # Wait for it to avoid it becoming orphan 
  •  
  •     celery_process.wait()  
  • 猜测

    UNIX 旌旗灯号处理是一个相当奇葩的过程——当过程收到一个旌旗灯号时,内核会选择一条线程(以必定的规矩),中断其当前控制流,将控制流强行转给旌旗灯号处理函数,待其履行完毕后再将控制流交还给原线程。时序图如下: 

    异常的 traceback 指向 celery 包,也就是嗣魅这是在 celery 主过程内部产生的异常

    因为控制流转换产生在同一条线程上,很多线程间同步机制会掉效甚至报错。是以旌旗灯号处理函数的编写要比线程函数加倍严格,对同一个文件输出是被禁止并且无解的,因为很可能会产生如许的工作:

    并且这个问题不克不及经由过程加锁来解决(因为是在同一个线程中,会逝世锁)。

    是以,我猜测异常产生时的事宜时序是如许的:在 print 未履行完时中断,又在旌旗灯号处理函数中调用 print,触发了重入检测,引起 RuntimeError:

     1/5    1 2 3 4 5 下一页 尾页

      推荐阅读

      CIO半月刊第十九期|李克强总理再提制造业转型,51CTO带你摸底制造业

    51CTO诚邀您9月23号和秒拍/国美/美团元专家一路聊智能CDN的优化之路,抓紧时光哦! 【编辑推荐】CIO半月刊第十四期|查访过30多位CIO,我们得出如许的结论CIO半月刊第十五期|某有名旅游企业>>>详细阅读


    本文标题:一个Reentrant Error引发的对Python信号机制的探索和思考

    地址:http://www.17bianji.com/lsqh/37408.html

    关键词: 探索发现

    乐购科技部分新闻及文章转载自互联网,供读者交流和学习,若有涉及作者版权等问题请及时与我们联系,以便更正、删除或按规定办理。感谢所有提供资讯的网站,欢迎各类媒体与乐购科技进行文章共享合作。

    网友点评
    自媒体专栏

    评论

    热度

    精彩导读
    栏目ID=71的表不存在(操作类型=0)