最近須要在一個基於nameko/eventlet的服務中集成grpc client, 遇到了一個monkeypatch帶來的兼容性問題, 測試代碼以下:python
import eventlet eventlet.monkey_patch(thread=True) import threading from grpc._cython import cygrpc class TestThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) def run(self): completion_queue = cygrpc.CompletionQueue() while True: _ = completion_queue.poll() threading._VERBOSE = True t = TestThread() t.start() print('if thread is not patched, this message will be printed')
當對thread模塊patch以後, 進程卡在了t.start(), 只有按ctrl+c中斷該線程以後, 程序才繼續運行. 但若是不對thread進行patch, 線程start以後, 程序繼續運行. 這是爲何呢?併發
使用pdb進行調試, 分爲兩種狀況:測試
程序在switch以後切換到TestThread運行, 彷佛就切換不回到主線程了!按下ctrl+c後TestThread才中斷, 並在主線程繼續運行.this
在TestThread進入start以後, self.__started.wait()直接返回, 值得注意的是, 在start內部調用_start_new_thread就直接啓動子線程, 而且直接返回了!
spa
可見monkeypatch修改了threading標準庫中的_start_new_thread方法, Condition類等. 當patch以後,_start_new_thread方法並不直接啓動線程, 而是返回一個greenlet, 在這個問題當中, grpc調用的是一個c extension中的threading pool, monkeypatch沒法對這個extension進行patch, 致使了後來switch到這個greenlet中時實際上是進入到另外一個線程中. 由於greenlet沒法在不一樣的線程中切換, 致使程序沒法從TestThread切回來, 只有主動去中斷TestThread, 才能被恢復.
自從遇到了這個問題, 之後作項目的併發模型就更加慎重了:). 若是不清楚monkeypatch到底作了些什麼, 在選擇協程作python的底層併發模式時, 請三思.線程