最近须要在一个基于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的底层并发模式时, 请三思.线程