python subprocess.Popen read阻塞问题解决
背景
使用subprocess.Popen打开一个子进程,指定子进程的标准输入,标准输出为subprocess.PIPE,使用stdout.read()读取子进程的标准输出,当子进程没有输出时read会导致程序阻塞。
原因分析
指定标准输入,标准输出,标准错误为subprocess.PIPE后,实则是使用匿名管道和子进程进行通信。匿名管道有两个非常重要的特性:
1.当写端一直写,而读端不去读,管道被写满后,写端继续写就会阻塞;
2.当写端打开不写数据,管道为空时,读端去读就会阻塞。
我们遇到的问题就是由上面第二个原因造成的。
解决思路
-
使用Popen.communicate()
在python的官方文档中提到为了避免管道缓冲被填满导致阻塞,可以使用Popen.communicate()来规避,并且可以传入timeout参数。
缺点:communicate()后便会关闭输入管道,无法和子进程继续交互,并且不是实时获取子进程输出。 -
使用非阻塞方式进行读取
网上查阅一番资料后,看到的使用非阻塞方式读取只在linux下有效。 -
从匿名管道本身入手
查看了windows关于匿名管道的api后发现了PeekNamedPipe()这个函数
使用该函数可以查看管道中是否有数据,当存在数据时再去进行读取,就可以避免由于管道没有数据造成的阻塞。
最终,步骤如下:
# 使用CreatePipe创建爱你匿名管道
#
import _winapi
import os
import subprocess
import time
SIZE = 128
# 创建匿名管道
...
...
# 使管道可以被子进程继承
...
...
# 使用管道描述符创建startupinfo
...
...
# 开启subprocess进程,使用PeekNamedPipe、ReadFile、GetOverlappedResult获取数据
...
...
版权声明:本文为weixin_41582874原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。