python subprocess.Popen read阻塞问题解决

  • Post author:
  • Post category:python




python subprocess.Popen read阻塞问题解决



背景

使用subprocess.Popen打开一个子进程,指定子进程的标准输入,标准输出为subprocess.PIPE,使用stdout.read()读取子进程的标准输出,当子进程没有输出时read会导致程序阻塞。



原因分析

指定标准输入,标准输出,标准错误为subprocess.PIPE后,实则是使用匿名管道和子进程进行通信。匿名管道有两个非常重要的特性:

1.当写端一直写,而读端不去读,管道被写满后,写端继续写就会阻塞;

2.当写端打开不写数据,管道为空时,读端去读就会阻塞。

我们遇到的问题就是由上面第二个原因造成的。



解决思路

  1. 使用Popen.communicate()

    在python的官方文档中提到为了避免管道缓冲被填满导致阻塞,可以使用Popen.communicate()来规避,并且可以传入timeout参数。

    缺点:communicate()后便会关闭输入管道,无法和子进程继续交互,并且不是实时获取子进程输出。
  2. 使用非阻塞方式进行读取

    网上查阅一番资料后,看到的使用非阻塞方式读取只在linux下有效。
  3. 从匿名管道本身入手

    查看了windows关于匿名管道的api后发现了PeekNamedPipe()这个函数

    使用该函数可以查看管道中是否有数据,当存在数据时再去进行读取,就可以避免由于管道没有数据造成的阻塞。

    PeekNamedPipe函数说明

    最终,步骤如下:
# 使用CreatePipe创建爱你匿名管道
# 
import _winapi
import os
import subprocess
import time

SIZE = 128

# 创建匿名管道
...
...
# 使管道可以被子进程继承
...
...
# 使用管道描述符创建startupinfo
...
...
# 开启subprocess进程,使用PeekNamedPipe、ReadFile、GetOverlappedResult获取数据
...
...



版权声明:本文为weixin_41582874原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。