You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

subprocess.Popen调用adb服务挂起问题:test2.py调用test.py为何阻塞?

为何用test2.py调用test.py时会在communicate()处挂起?

你遇到的这个问题是典型的管道缓冲区死锁导致的,我来帮你拆解清楚原因,再给出具体的解决办法。

问题根源:双向等待的死锁

我们一步步看为什么会卡住:

  1. 当test2.py用Popen("python test.py", stdout=PIPE, stderr=PIPE, stdin=PIPE)启动test.py时,test.py的所有标准输出(stdout)和错误输出(stderr)都被重定向到了test2.py创建的管道里。
  2. test.py里用os.system("adb kill-server")执行命令,这个命令的输出(不管是正常信息还是错误提示)会直接写入test.py的stdout/stderr,也就是上面说的管道。
  3. 管道的缓冲区大小是有限的,当adb kill-server的输出填满缓冲区后,os.system会阻塞——因为它要等管道有空闲空间才能继续写数据。但此时test2.py正在调用p.communicate(),它在等test.py执行完并输出所有数据。
  4. 这就形成了死锁:test.py卡在os.system等着输出被读取,test2.py卡在communicate()等着test.py跑完,谁也动不了。

另外,你的代码还有几个小冗余点,虽然不是直接原因,但也可以优化:

  • test.py里的p.wait()和p.terminate()完全多余,因为p.communicate()已经会自动等待子进程结束,并关闭所有管道。
  • test2.py里调用python test.py时,用列表形式的参数(["python", "test.py"])比字符串更安全,跨平台兼容性更好。

解决方案:切断死锁的源头

要解决这个问题,核心就是不让adb kill-server的输出占用管道缓冲区,有两种简单的实现方式:

方案1:重定向adb kill-server的输出

直接把adb kill-server的输出丢弃,不让它流入管道:

# 如果你用Linux/macOS
os.system("adb kill-server > /dev/null 2>&1")
# 如果你用Windows
os.system("adb kill-server > NUL 2>&1")

> /dev/null 2>&1(或Windows的> NUL 2>&1)的意思是把标准输出和错误输出都丢到“黑洞”里,不会占用任何缓冲区。

方案2:用subprocess.Popen替代os.system

os.system的输出控制不够灵活,换成subprocess.Popen可以更精准地处理:

from subprocess import Popen, DEVNULL
# 直接丢弃adb kill-server的所有输出
Popen("adb kill-server", stdout=DEVNULL, stderr=DEVNULL).wait()

DEVNULL就是专门用来丢弃输出的参数,比重定向命令更简洁,跨平台也不用区分系统。

额外优化建议

修改后的test.py可以改成这样(顺便去掉冗余代码):

import os
from subprocess import Popen, PIPE, DEVNULL

# 替代os.system,丢弃adb kill-server的输出
Popen("adb kill-server", stdout=DEVNULL, stderr=DEVNULL).wait()

p = Popen("adb devices", stdout=PIPE, stderr=PIPE, stdin=PIPE)
stdout_data, _ = p.communicate()
# 注意:Python 3中communicate返回字节串,需要解码;Python 2直接用字符串即可
for line in stdout_data.split(b"\r\n"):
    print(line.decode("utf-8").strip())

同时test2.py也可以优化命令参数的写法:

import os
from subprocess import Popen,PIPE
# 用列表形式的命令参数,更安全
p = Popen(["python", "test.py"], stdout=PIPE, stderr=PIPE, stdin=PIPE)
stdout_data, _ = p.communicate()
for line in stdout_data.split(b"\r\n"):
    print(line.decode("utf-8").strip())
# 同样,这里的p.wait()和p.terminate()也可以去掉

这样修改后,test2.py调用test.py时就不会再出现挂起的情况了。

内容的提问来源于stack exchange,提问作者Huang Jianan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:35:18