python学习笔记之--多进程编程

2022-04-13  本文已影响0人  itsenlin

简介

前面学习了python语言的多线程编程,由于全局解释器锁的引入,多线程编程无法充分利用多CPU的性能。而python提供了一个与多线程编程类似的API的包实现多进程编程模块--multiprocessing,多进程没有全局解释器锁的限制,也即可以利用多CPU的性能。

multiprocessing模块

根据不同的平台, multiprocessing支持三种启动进程的方法。

进程启动方式 适用平台 说明
spawn windowns/unix 父进程会启动一个全新的 python 解释器进程。 子进程将只继承那些运行进程对象的 run()方法所必需的资源。
fork unix 使用os.fork()生成Python解析器fork分支,此方式在多线程场景下不安全
forkserver unix 启动一个服务器进程,每当需要一个新进程时,父进程就会连接到服务器并请求它fork一个新进程。服务器进程是单线程的,因此使用 os.fork()是安全的。

可以通过模块的接口set_start_method()来改变进程启动的方式,注意,此接口只能调用一次。

此模块还提供了所有与threading类似的同步原语,以及进程间通讯方式,详细信息参见官方文档

subprocess模块

python语言还提供了一个subprocess模块,此模块主要是生成新的进程,连接它们的输入、输出、错误管道,并且获取它们的返回码。打算代替一些老旧的模块与功能:

os.system()
os.spwan*()

此模块提供了两种生成子进程的接口:run()Popen; 后者比较底层一些,灵活性更强。前者底层也是用后者来实现的。

Popen类

原型如下:

class subprocess.Popen(args, bufsize=- 1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=None, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, group=None, extra_groups=None, user=None, umask=- 1, encoding=None, errors=None, text=None, pipesize=- 1)

有很多个默认参数,这也是为什么说Popen接口比较灵活的原因了。在unix平台下使用类似于os.execvpe接口实现;而在windowns下使用CreateProcess()来实现。

常用的类方法

Popen类提供了很多很有用的方法,常用的有以下几个

方法 说明
wait(timeout=None) 等待子进程被终止。设置并返回 returncode属性。使用管道时有可能产生死锁,可以使用communicate接口来规避。
communicate(input=None, timeout=None) 与进程交互:将数据发送到 stdin。 从 stdout 和 stderr 读取数据,直到抵达文件结尾。 等待进程终止并设置 returncode属性。
poll() 检查子进程是否已被终止。设置并返回 returncode属性。否则返回 None。此接口不阻塞。

还有以下几个常用的属性

属性 说明
Popen.stdin 如果 stdin 参数为 PIPE,此属性是一个类似 open()返回的可写的流对象。
Popen.stdout 如果 stdout 参数为 PIPE,此属性是一个类似 open()返回的可读的流对象。从流中读取子进程提供的输出。
Popen.stderr 如果 stderr 参数为 PIPE,此属性是一个类似 open()返回的可写的流对象。从流中读取子进程提供的错误信息。如果执行正常则返回空字符串。
Popen.returncode 此进程的退出码,由 poll()wait() 设置(以及直接由 communicate()设置)。一个 None 值 表示此进程仍未结束。
>>> p = subprocess.Popen("ls -l /dev/null", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> p.stdout.read()
b'crw-rw-rw- 1 root root 1, 3 4\xe6\x9c\x88  13 21:49 /dev/null\n'
>>> p.stderr.read()
b''
>>> p.returncode
0
>>> 

subprocess.run函数接口

接口原型如下

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, capture_output=False, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None, **other_popen_kwargs)

此接口返回subprocess.CompletedProcess类的一个对象,也表示子进程运行结束。此类提供以下属性与方法:

属性/方法 说明
args 被用作启动进程的参数. 可能是一个列表或字符串。
returncode 子进程的退出状态码. 通常来说, 一个为 0 的退出码表示进程运行正常。
stdout 从子进程捕获到的标准输出. 一个字节序列, 或一个字符串。
stderr 捕获到的子进程的标准错误. 一个字节序列, 或者一个字符串。
check_returncode() 如果 returncode非零, 抛出 CalledProcessError异常。
>>> p = subprocess.run("ls -l /dev/null", shell=True, capture_output=True)
>>> p.stdout
b'crw-rw-rw- 1 root root 1, 3 4\xe6\x9c\x88  13 21:49 /dev/null\n'
>>> p.stderr
b''
>>> p.returncode
0
>>> 

总结

python的multiprocessing模块和subprocess模块都可以创建子进程,两者还是有很大的区别的。

上一篇 下一篇

猜你喜欢

热点阅读