凉城旧巷
Python从入门到自闭,Java从自闭到放弃,数据库从删库到跑路,Linux从rm -rf到完犊子!!!

Gevent和Subprocess问题

1、复现

在main文件中调用gevent、并做了monkey pathch, 然后再调用subprocess.Popen(),出现一直卡住的问题

  • Python 标准库里的很多 IO(网络 socket、ssl、time.sleep 等)都是 阻塞的
    • 调用 socket.recv() 会阻塞整个线程,其他 greenlet 无法运行。
  • 为了让这些阻塞调用 在等待时让出控制权,gevent 需要“接管”标准库里的阻塞函数 → 这就是 monkey patch
from gevent import monkey
monkey.patch_all()


import subprocess


p = subprocess.Popen("echo 111", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

p.wait()

2、原因

monkey.patch_all() 会对 Python 的标准库做打猴子补丁,其中包括 subprocess。而被打过补丁的 subprocess 在某些情况下(尤其是涉及到 Popencommunicatewait 或交互式命令)会出现 阻塞/卡住 的情况

  • subprocess 被 gevent 补丁后不完全兼容
    gevent 会用协程友好的 IO 替换底层调用,但 subprocess 的管道和底层 select/poll 行为会变得不可预期。
  • 所有 Popencommunicatewait 都是标准阻塞版本,不会和 gevent 的事件循环配合

3、解决方法

以下是两种解决办法, 确认是否需要协程化的子进程

  • 如果只是跑系统命令、不需要协程化,建议用 原生 subprocess,用方法1
  • 如果需要和 gevent 事件循环兼容,再考虑 gevent.subprocess, 用方法2

方法1:不要 monkey patch subprocess
在调用 monkey.patch_all() 时,可以显式排除:

from gevent import monkey
monkey.patch_all(subprocess=False)

 

方法2:使用 gevent.subprocess 代替
如果你确实想在 gevent 下用子进程,可以用:

from gevent import subprocess
p = subprocess.Popen(["cmd", "/c", "echo hello"], stdout=subprocess.PIPE)
out, err = p.communicate()
print(out.decode())

这个是 gevent 提供的封装,适配了它的事件循环。

posted on 2025-09-22 13:14  凉城旧巷  阅读(22)  评论(0)    收藏  举报