Python多进程 - subprocess & multiprocess

Posted __i视觉__(self, 知识库):

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python多进程 - subprocess & multiprocess相关的知识,希望对你有一定的参考价值。

1. subprocess

homepage

博客: 使用subprocess模块调用子进程并获取输出

该模块主要用于调用子程序

  • multiprocess: 是同一个代码中通过多进程调用其他的模块(也是自己写的)
  • subprocess: 直接调用外部的二进制程序,而非代码模块

1.1. Popen

p.poll()  # 检查进程是否终止,如果终止返回returncode,否则返回None
p.wait(timeout)  # 等待子进程终止(阻塞父进程)
p.communicate(input,timeout)  # 和子进程交互,发送和读取数据(阻塞父进程)
p.send_signal(singnal)  # 发送信号到子进程
p.terminate()  # 停止子进程,也就是发送SIGTERM信号到子进程
p.kill()  # 杀死子进程。发送SIGKILL信号到子进程

创建Popen对象后,主程序不会自动等待子进程完成

以上三个成员函数都可以用于等待子进程返回:while循环配合Popen.poll()、Popen.wait()、Popen.communicate()。由于后面二者都会阻塞父进程,所以无法实时获取子进程输出,而是等待子进程结束后一并输出所有打印信息。另外,Popen.wait()、Popen.communicate()分别将输出存放于管道内存,前者容易超出默认大小而导致死锁,因此不推荐使用。

注意:p.communicate(stdin="xxx") 该函数会终止子程序(因为其是阻塞的,当父程序解除阻塞时,意味着子程序已经结束了)。所以,如果你的子程序是 while... 或者 for line in sys.stdin 时,你会发现子程序意外的结束了,而不是在循环中等待。

1.1.1. 管理子进程(通信)

Popen类具有三个与输入输出相关的属性:Popen.stdin , Popen.stdoutPopen.stderr ,分别对应子进程的标准输入/输出/错误。它们的值可以是PIPE、文件描述符(正整数)、文件对象或None

  • PIPE表示创建一个连接子进程的新管道,默认值None, 表示不做重定向。
  • 子进程的文件句柄可以从父进程中继承得到。
  • stderr 可以设置为 STDOUT ,表示将子进程的标准错误重定向到标准输出。
child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
child2 = subprocess.Popen(["wc"], stdin=child1.stdout, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
out = child2.communicate()

其中,subprocess.PIPE为文本流提供一个缓存区,child1的stdout将文本输出到缓存区;随后child2的stdin从该PIPE读取文本,child2的输出文本也被存放在PIPE中,而标准错误信息则重定向到标准输出;最后,communicate()方法从PIPE中读取child2子进程的标准输出和标准错误。

注意:subprocess.stdxxx 操作bytes字节,而 sys.stdin 则是string。

sys.stdin

Python的sys模块定义了标准输入/输出/错误:

sys.stdin  # 标准输入
sys.stdout # 标准输出
sys.stderr # 标准错误信息

以上三个对象类似于文件流,因此可以使用readline()和write()方法进行读写操作。也可以使用print(),等效于sys.stdout.write()。

需要注意的是,除了直接向控制台打印输出外,标准输出/错误的打印存在缓存,为了实时输出打印信息,需要执行

sys.stdout.flush()
sys.stderr.flush()

读取 sys.stdin 的方式:

for line in sys.stdin:
    print(type(line))  # string
    ...
示例

前面提到,如果子程序只是调用一次,并获取其输出状态,可以使用 p.communicate(stdin, timeout) 结合 p.returncode 实现。

但如果你的程序想实现类似 TCP-C/S 式的持续通信服务,这里提供一个Demo:

父程序:

proc = subprocess.Popen(command,
                        stdin=subprocess.PIPE,
                        stdout=subprocess.PIPE)
                        # stderr=subprocess.STDOUT)

# try:
while proc.poll() is None:  # 持续输入
    str_input = input("Please Input a path: ")
    if str_input == "quit":
        break

    bytes_path = f"/home/brt/{str_input}.jpg
".encode()  # 注意这里需要
换行符
    proc.stdin.write(bytes_path)  # 需要使用bytes
    proc.stdin.flush()
    # proc.communicate(stdin=str_input, timout=5)

    bytes_state = proc.stdout.readline()  # bytes
    if bytes_state == b"ok
":
        print("Well Done.")

# except subprocess.TimeoutExpired:
#     print("子程序Timout未响应...")
#     break

# if proc.poll() is None:  # communicate()超时时,子程序可能未退出
#     proc.kill()

子程序:

for path_save in sys.stdin:  # 持续读取
    path_save = path_save.strip()  # 删除多余的换行符
    img = grabclipboard_byQt(cb)
    # sys.stderr.write(">>>", img)
    if img:
        save_clipboard_image(img, path_save)
        str_pipe = "ok"
    else:
        str_pipe = ""

    # 以下内容用于写入stdout管道,向父程序反馈
    # sys.stdout.write(str_pipe + "
")  # 必须添加换行符
    print(str_pipe)
    sys.stdout.flush()  # 及时清空缓存

2. multiprocess

以上是关于Python多进程 - subprocess & multiprocess的主要内容,如果未能解决你的问题,请参考以下文章

2022-04-11 - Python multiprocessing subprocess 模块区别

Python标准库10 多进程初步 (multiprocessing包)

python子进程模块subprocess详解与应用实例 之二

python 怎么启动一个外部命令程序,并且不阻塞当前进程

python 模块积累-----subprocess

python模块--subprocess