subprocess.Popen() 多进程

subprocess.Popen() 用法

subprocess.Popen()主要是用来在 python 中实现多进程程序。例如,在 python 脚本中,我们需要执行另一个 python 脚本,或者执行 shell 命令或者 shell 脚本,这种情况下就要用到 python 的多进程方法了。本文仅介绍 subprocess.Popen() 方法.

subprocess.Popen() 的使用格式:

class subprocess.Popen( 
	  args, 
      bufsize=0, 
      executable=None,
      stdin=None,
      stdout=None, 
      stderr=None, 
      preexec_fn=None, 
      close_fds=False, 
      shell=False, 
      cwd=None, 
      env=None, 
      universal_newlines=False, 
      startupinfo=None, 
      creationflags=0)

各个参数的含义:
subprocess.Popen() 多进程

subprocess.PIPE 可以初始化 stdin, stdout 或 stderr 参数。表示与子进程通信的标准流
subprocess.STDOUT 用于初始化 stderr 参数,表示将错误通过标准输出流输出。

Popen 的属性

subprocess.Popen() 多进程 subprocess.Popen() 多进程

子进程结束的判断

首先来看一段代码:

import subprocess as sp
p = sp.Popen(['echo','helloworl.py'], stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE)
print(p.poll())
print('Exit code:', p.returncode)

打印结果如下,在上述代码中我们仅仅创建了一个 Popen 对象,然后开启了子进程,随后通过 poll() 和 returncode 查看子进程的返回码,从结果来看,子进程没有结束主进程就退出了。

None
Exit code: None

下面我们通过读取 stdout 的内容来看看子进程会不会正常结束:

import subprocess as sp
p = sp.Popen(['echo','helloword.py'], stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE)
print((p.stdout.readlines())[0].decode(), end='')
print(p.poll())
print('Exit code:', p.returncode)

结果如下,可以看到,结果仍然是子进程没有结束。

helloword.py
None
Exit code: None

前面说到,wait 会等待子进程结束,那么我们试试执行 wait 看看:

import subprocess as sp
p = sp.Popen(['echo','helloword.py'], stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE)
print(p.poll())
print(p.wait())
print('Exit code:', p.returncode)

结果可以看到,在 wait 之前子进程并没有结束,wait 执行返回的值 0,returncode 也是 0,表明子进程正常执行并结束了。

None
0
Exit code: 0

虽然 wait 可以让子进程正常结束,但是如果缓存中太多数据的话会导致死锁,因此我们采用 communicate:

import subprocess as sp
p = sp.Popen(['echo','helloword.py'], stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE)
output, err = p.communicate()
print(output.decode('gbk'), end='')
print(err.decode('gbk'))
print(p.poll())
print('Exit code:', p.returncode)

结果如下,可见在 communicate 之后,子进程正常执行,缓存中的数据被读取出来,poll 和 returncode 都返回 0,表明子进程已正常结束。output 是子进程执行输出的信息,err 是执行异常时的报错信息,此处可以看到报错信息为空,说明子进程顺利执行,没有错误发生。

helloword.py

0
Exit code: 0

今天发现一个有意思的现象,当 p.stdout.readlines()读取了内容之后,再使用 p.communicate() 读取子进程缓存中的内容,发现后者已经没有内容了,代码如下:

import subprocess as sp
p = sp.Popen(['echo','helloworl.py'], stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE)
print(p.poll())
print('Exit code:', p.returncode)
print((p.stdout.readlines())[0].decode(), end='')
output, err = p.communicate()
print(output.decode('gbk'), end='')
print(err.decode('gbk'))
# p.terminate()
print(p.poll())
print('Exit code:', p.returncode)

结果如下:

None
Exit code: None
helloworl.py

0
Exit code: 0

结果可以看出,p.communicate()没有读取到内容,猜测可能是 p.stdout.readlines() 已经将内容读取出来,因而管道中已经没有内容了,但是前面讲到 p.stdout.readlines()读取了内容之后进程并不能正常结束,只有 p.communicate() 可以让进程正常结束。那么在碰到需要用 p.stdout.readlines()的时候,我们可以采用 terminate() 让进程强制结束。

import subprocess as sp
p = sp.Popen(['echo','helloworl.py'], stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE)
print(p.poll())
print('Exit code:', p.returncode)
print((p.stdout.readlines())[0].decode(), end='')
p.terminate()
print(p.poll())
print('Exit code:', p.returncode)

结果如下:

None
Exit code: None
helloworl.py
0
Exit code: 0