36_并发编程-multiprocess模块
仔细说来,multiprocess不是一个模块而是python中一个操作、管理进程的包。 之所以叫multi是取自multiple的多功能的意思,在这个包中几乎包含了和进程有关的所有子模块。由于提供的子模块非常多,为了方便大家归类记忆,我将这部分大致分为四个部分:创建进程部分,进程同步部分,进程池部分,进程之间数据共享。重点强调:进程没有任何共享状态,进程修改的数据,改动仅限于该进程内,但是通过一些特殊的方法,可以实现进程之间数据的共享。
一、Process模块
1、模块介绍 - process模块是一个创建进程的模块,借助这个模块,就可以完成进程的创建。
模块介绍
1 Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
2
3 强调:
4 1. 需要使用关键字的方式来指定参数
5 2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
6
7 参数介绍:
8 group参数未使用,值始终为None
9 target表示调用对象,即子进程要执行的任务
10 args表示调用对象的位置参数元组,args=(1,2,'egon',)
11 kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
12 name为子进程的名称
13 pid 子进程的pid
2、Process模块中方法介绍
其他方法
1 p.start():启动进程,并调用该子进程中的p.run();给操作系统发了一个启动本进程p的信号
2 p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
3 p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁,给操作系统发了终止程序的信号
4 p.is_alive():如果p仍然运行,返回True
5 p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程
3、Process模块属性介绍
模块属性
View Code
1 p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
2 p.name:进程的名称
3 p.pid:进程的pid
4 p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
5 p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)
4、在Windows中使用Process的注意事项
在Windows操作系统中由于没有fork(linux操作系统中创建进程的机制),在创建子进程的时候会自动 import 启动它的这个文件,而在 import 的时候又执行了整个文件。因此如果将process()直接写在文件中就会无限递归创建子进程报错。所以必须把创建子进程的部分使用if __name__ ==‘__main__’ 判断保护起来,import 的时候,就不会递归运行了。
5、实例
1 import os
2 import time
3 from multiprocessing import Process
4
5 def func(father_id):
6 print(father_id) # 打印爷爷id
7 print(os.getppid()) # 打印父亲的进程id
8 print(os.getpid()) # 打印自己的进程id
9 time.sleep(10)
10
11 if __name__ == '__main__':
12 print('主进程开始啦!!!')
13 main_father = os.getppid() # 打印主进程的父进程
14 print(main_father)
15 p = Process(target=func, args=(main_father,)) # 创建一个进程, 掉func函数,传入main_father参数 args()传入的是元组
16 print(os.getpid()) # 打印自己的进程id
17 p.start() # 启动子进程
18 print('哈,结束了!')
二、详细描述
详细描述
1 # 当前文件名称为test.py
2 from multiprocessing import Process
3
4 def func():
5 print(12345)
6
7 if __name__ == '__main__': #windows 下才需要写这个,这和系统创建进程的机制有关系,不用深究,记着windows下要写就好啦
8 #首先我运行当前这个test.py文件,运行这个文件的程序,那么就产生了进程,这个进程我们称为主进程
9
10 p = Process(target=func,) #将函数注册到一个进程中,p是一个进程对象,此时还没有启动进程,只是创建了一个进程对象。并且func是不加括号的,因为加上括号这个函数就直接运行了对吧。
11 p.start() #告诉操作系统,给我开启一个进程,func这个函数就被我们新开的这个进程执行了,而这个进程是我主进程运行过程中创建出来的,所以称这个新创建的进程为主进程的子进程,而主进程又可 以称为这个新进程的父进程。
12 #而这个子进程中执行的程序,相当于将现在这个test.py文件中的程序copy到一个你看不到的python文件中去执行了,就相当于当前这个文件,被另外一个py文件import过去并执行了。
13 #start并不是直接就去执行了,我们知道进程有三个状态,进程会进入进程的三个状态,就绪,(被调度,也就是时间片切换到它的时候)执行,阻塞,并且在这个三个状态之间不断的转换,等 待cpu执行时间片到了。
14 print('*' * 10) #这是主进程的程序,上面开启的子进程的程序是和主进程的程序同时运行的,我们称为异步
1、主进程和子进程
主进程子进程
1 import time
2 import os
3
4 #os.getpid() 获取自己进程的ID号
5 #os.getppid() 获取自己进程的父进程的ID号
6
7 from multiprocessing import Process
8
9 def func():
10 print('aaaa')
11 time.sleep(1)
12 print('子进程>>',os.getpid())
13 print('该子进程的父进程>>',os.getppid())
14 print(12345)
15
16 if __name__ == '__main__':
17 #首先我运行当前这个文件,运行的这个文件的程序,那么就产生了主进程
18
19 p = Process(target=func,)
20 p.start()
21 print('*' * 10)
22 print('父进程>>',os.getpid())
23 print('父进程的父进程>>',os.getppid())
24
25 #加上time和进程号给大家看一看结果:
26 #********** 首先打印出来了出进程的程序,然后打印的是子进程的,也就是子进程是异步执行的,相当于主进程和子进程同时运行着,如果是同步的话,我们先执行的是func(),然后再打印主进程最后的10个*号。
27 #父进程>> 3308
28 #父进程的父进程>> 5916 #我运行的test.py文件的父进程号,它是pycharm的进程号,看下面的截图
29
30 #aaaa
31 #子进程>> 4536
32 #该子进程的父进程>> 3308 #是我主进程的ID号,说明主进程为它的父进程
33 #12345
2、windows系统特性
(1)一个进程的生命周期:如果子进程的运行时间长,那么等到子进程执行结束程序才结束,如果主进程的执行时间长,那么主进程执行结束程序才结束,实际上我们在子进程中打印的内容是在主进程的执行结果中看不出来的,但是pycharm帮我们做了优化,因为它会识别到你这是开的子进程,帮你把子进程中打印的内容打印到了显示台上。
(2)如果说一个主进程运行完了之后,我们把pycharm关了,但是子进程还没有执行结束,那么子进程还存在吗?这要看你的进程是如何配置的,如果说我们没有配置说我主进程结束,子进程要跟着结束,那么主进程结束的时候,子进程是不会跟着结束的,他会自己执行完,如果我设定的是主进程结束,子进程必须跟着结束,那么就不会出现单独的子进程(孤儿进程)了,具体如何设置,看下面的守护进程的讲解。比如说,我们将来启动项目的时候,可能通过cmd来启动,那么我cmd关闭了你的项目就会关闭吗,不会的,因为你的项目不能停止对外的服务,对吧。