Python之路 - 多进程编程
前言 🍀
上一篇《多线程编程》中已经对Python中多线程部分进行了整理 , 进程中有很多也是相似的
概念在并发编程第一篇中就已经介绍了 , So直接开始操作
multiprocessing 🍀
从上一篇我们也已经知道了 , Python中的多线程无法利用多核优势 , 所以如果我们想要充分地使用多核CPU的资源 , 那么就只能靠多进程了 , 因为进程是系统调度的 , Python提供了multiprocessing
模块了对多进程的支持
multiprocessing模块中提供了Process , Queue , Pipe , Lock , RLock , Event , Condition等组件 , 与threading模块有很多相似之处
Process 🍀
用于创建进程的类 , 与threading模块中的_Thread
类类似
1 | ''' |
参数说明
参数 | 说明 |
---|---|
group | 未使用 , 值始终 |
target | 与threading.Tread中的target参数一样 , 表示调用对象 , 即子进程要执行的任务 |
name | 子进程的名称 |
args | 传入target函数中的位置参数 , 是一个元组 , 与线程一样 , 参数后必须加逗号 |
kwargs | 表示调用对象的字典 |
方法说明
方法 | 说明 |
---|---|
Process.run (self) | 进程启动时运行的方法 , 由该方法调用target参数所指定的函数 , 在子类中可以进行重构 , 与线程中一样 |
Process.start (self) | 启动进程 , start方法就是去帮你调用run方法 |
Process.terminate (self) | 强制终止进程 , 不会进行任何清理操作 , 使用时需小心其子进程与锁的问题 |
Process.join (self, timeout=None) | 与线程中一样 , 阻塞调用 , 主进程进行等待 , timeout为超时时间 |
Process.is_alive (self) | 判断进程是否正在运行 , 返回bool值 |
实例属性说明
属性 | 说明 |
---|---|
Process.daemon | 默认值为False , True则为守护进程 |
Process.name | 进程的名称 |
Process.pid | 进程的pid |
Process.exitcode | 进程运行时为None , 如果为-N , 表示被信号N结束 |
Process.authkey | 进程的身份验证键 , 默认是由os.urandom()随机生成的32字符的字符串 . 这个键的用途是为涉及网络连接的底层进程间通信提供安全性 , 这类连接只有在具有相同的身份验证键时才能成功 |
创建进程
与创建线程的方式一样 , 有两种
函数调用
1 | import multiprocessing |
类继承调用
1 | import multiprocessing |
在上栗创建进程中有一个问题 , 就是如果我们在Windows下 , 使用start()
方法 , 就必须加上if __name__ == '__main__':
, 进程是通过fork
系统调用 , 而Windows中并没有fork , 所以多处理模块启动了一个新的Python进程 , 并导入了调用模块 . 如果进程在导入的时候被调用 , 那么这就会引发无限的新进程 , 后果不言而喻 . 当然还是可以直接使用run()
的
Join & Daemon 🍀
join
进程中join与线程中的join是一样的 , 就进行阻塞调用 , 让主进程进行等待 , 整体串行
实例
1 | # 多线程中的例子,换汤不换药 |
Daemon
守护进程会在主进程代码执行结束后就终止
1 | # 还是多线程中的例子 |
PS : 与线程不同的是 , 守护进程内无法再开启子进程 , 否则就抛出异常
Lock 🍀
进程之间的数据是不共享的 , 因为每个进程之间是相互独立的 , 但是进程共享一套文件系统 , 所以访问同一个文件 , 是没有问题的 , 但是如果有多个进程对同一文件进行修改 , 就会造成错乱 , 所以我们为了保护文件数据的安全 , 就需要给其进行加锁
同样的 , join为整体串行 , lock为局部串行
廖大大实例 , Lock
1 | import multiprocessing |
RLock
1 | import multiprocessing |
Producer-consumer 🍀
生产者消费者模式 , 在多线程中已经有过说明了 , 目的是为了解决并发问题
实例
1 | # 可与多线程篇中进行对照 |
Queue 🍀
multiprocessing模块支持进程间通信有两种主要形式 , 队列和管道
在多线程中有queue模块 , 供我们实现队列接口 , 在多进程中则是Queue类为我们提供队列接口
Queue为单向通道 , 先进先出(FIFO)
1 |
|
实例
1 | import multiprocessing |
Pipe 🍀
介绍
1 | # Pipe在进程之间创建一条管道,并返回元组(connection(),connection()) |
基于管道实现进程间通信
1 | import multiprocessing |
Manager 🍀
进程之间是相互独立的 , 在multiprocessing模块中的Manager可以实现进程间数据共享 , 并且Manager还支持进程中的很多操作 , 比如Condition , Lock , Namespace , Queue , RLock , Semaphore等
由于基于消息传递(Queue , Pipe)的并发编程才是未来的主流 , 所以对于Manager应该尽量避免使用
Manager实例
1 | import multiprocessing |
更多详细内容< multiprocessing.Manager >
Semaphore 🍀
与线程中一样
1 | class Semaphore(object): |
实例
1 | import multiprocessing |
Event 🍀
与线程中一样
1 | class Event(object): |
实例
1 | import multiprocessing |
Pool 🍀
multiprocessing
中的Process实现了我们对多进程的需求 , 但是当我们进行并发编程时 , 一旦需要开启的进程数量非常大时 , 使用Process已经不能满足我们的要求了 . 因为进程是需要占用系统资源的 , 操作系统不可能去无限的开启进程 ; 并且使用Process动态生成多个进程 , 我们还需要手动的去限制进程的数量 , 所以这个时候我们就应该用进程池(Pool)来实现了
multiprocessing.Pool
参数说明
参数 | 说明 |
---|---|
numprocess | 要创建的进程数 , 如果省略 将默认使用cpu_count() |
initializer | 每个进程启动时要执行的可调用对象 |
initargs | 传给initializer的参数组 |
方法说明
方法 | 说明 |
---|---|
Pool.apply(self, func, args=(), kwds={}) | 在一个进程池中执行func(*args , **kwargs) , 并返回结果 |
Pool.apply_async(self, func, args=(), kwds={}, callback=None, | 与apply()方法一样 , 该方法为异步版本应用的方法 , 返回结果是AsyncResult类的实例 , callback指定回调的函数 . callback禁止执行任何阻塞操作 , 否则将接收其他异步操作中的结果 |
Pool.close(self) | 关闭进程池 , 如果所有操作持续挂起 , 它们将在工作进程终止前完成 |
Pool.join(self) | 等待所有工作进程退出 |
Pool.get(self, timeout=None) | 获取结果 , timeout可选 |
Pool.ready(self) | 完成调用就返回True |
Pool.successful(self) | 完成调用并且没有引发异常返回True , 在结果就绪之前调用此方法会引发异常 |
Pool.wait(self, timeout=None) | 等待结果变为可用 |
Pool.terminate(self) | 立即终止所有工作进程 , 垃圾回收会自动调用此方法 |
同步调用apply
1 | from multiprocessing import Pool |
异步调用apply_async
1 | from multiprocessing import Pool |