代碼乾貨 | 一個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 主進程內部發生的異常

這個結果大大出乎了我的意料。隨機性異常是衆多最難纏的問題之一,因爲這常常意味着併發問題,涉及底層知識,病竈隱蔽,調試難度大,同時沒有有效的手段判斷問題是否徹底解決(可能只是降低了頻率)。


 展開全文

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