目录
Python如何使用多进程
- 创建子进程的方式
1.导入multiprocessing中的Process类,并且实例化这个类,指定要执行的任务(如函数)target
import os
from multiprocessing import Process
# Process就表示进程,在很多地方都是这样
def task():
print('this is sub process')
print(f'sub process id {os.getpid()}')
# 注意,开启进程的代码必须放在__main__判断下面,不然会循环导入,下面会详细解释
if __name__ == '__main__':
# 实例化一个进程对象
p = Process(target=task)
p.start() # 给操作系统发送指令,让它开启进程
# 如果子进程只使用一次且没什么操作,可以直接这样用
# Process(target=task).start()
print('this is parent process')
print(f'parent process is {os.getpid()}')
this is parent process
parent process is 2060
this is sub process
sub process id 10008
为什么要写在main下面
linux 与 windows 开启进程的方式不同
linux 会将父进程的内存数据完整copy一份给子进程
windows 会导入父进程的代码,并且从头执行一遍,来获取需要处理的任务
所以在编写代码时一定要将开启进程的代码放在main判断中,否则就会循环导入
2.导入multiprocessing 中的Process类,继承这个类,覆盖run方法,将要执行的任务放入run中,开启进程时则会自动执行该函数
- 在需要对进程对象进行高度自定义时使用
from multiprocessing import Process
import os
class Downloader(Process):
# def __init__(self, url, size, name):
# super().__init__()
# self.url = url
# self.size = size
# self.name = name
def run(self):
print(os.getpid())
if __name__ == '__main__':
m = Downloader()
m.start() # 这里不要使用run(),因为那样只是运行方法而不是开启进程
print('parent over', os.getpid())
parent over 1756 # 父进程打印的
parent over 4048 # 子进程打印的
4048 # 子进程的run()中打印的
从这个例子的打印顺序中可以得知:开启子进程需要时间,所以会先执行父进程
进程之间相互隔离
1.可以在开启进程后使用sleep方法,但是这样做并不好,因为你无法确定这个子进程到底会运行多久
join函数
- 主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
from multiprocessing import Process
def task1(name):
for i in range(100):
print(f'{name} run')
def task2(name):
for i in range(100):
print(f'{name} run')
if __name__ == '__main__': # args 是给子进程传参的,必须是元祖
p1 = Process(target=task1, args=('p1',))
p1.start()
p2 = Process(target=task2, args=('p2',))
p2.start()
p2.join() # 让主进程,等待子进程执行完毕后再继续执行
p1.join()
# 达到的效果是,保证两个子进程是并发执行的,并且over一定是在最后打印
print('over')
案例:
from multiprocessing import Process
def task1(name):
for i in range(10):
print(f'{name} run')
if __name__ == '__main__':
ps=[] # 需要把每一个进程添加到列表中
for i in range(10):
p = Process(target=task1, args=(i,))
p.start()
ps.append(p)
for i in ps: # 遍历列表,保证每一个子进程都已经结束了
i.join()
# 达到的效果是,把100个打印,分给10个子进程去做,最后打印over
print('over')
进程对象的常用属性(了解)
方法名 | 作用 |
---|---|
p.name | 打印进程名,可以在实例化时添加name参数 |
p.daemon | 守护进程 |
p.exitcode | 获取进程的退出码(就是exit()函数中传入的值) |
p.is_alive() | 查看进程是否存活 |
p.pid() | 获取进程id(子进程) |
p.terminate() | 终止进程,与start相同,不会立即终极,操作系统需要时间 |
僵尸进程与孤儿进程(了解)
孤儿进程 当父进程已经结束,而子进程还在运行,子进程就称为孤儿进程,比如用QQ打开了浏览器,再退掉QQ,那么浏览器就是孤儿进程,这个有其存在的必要性,没有不良影响,孤儿进程会被操作系统所接管
僵尸进程 当一个进程已经结束了,但是它仍然还有一些数据存在,此时称之为僵尸进程
在linux中,有这么一个机制,父进程无论什么时候都可以获取到子进程的一些数据
子进程任务执行完毕后,确实结束了但是仍然保留一些数据,目的是为了让父进程能够获取信息
linux中,可以调用waitpid来彻底释放子进程的残留信息