多进程
1, multiprocessing模块介绍
- multiprocessing模块用来开启子进程,并在子进程中执行我们定制的任务(比如函数),该模块与多线程模块threading的编程接口类似。
- multiprocessing模块的功能众多:支持子进程、通信和共享数据、执行不同形式的同步,提供了Process、Queue、Pipe、Lock等组件。
- 需要再次强调的一点是:与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限于该进程内
2,Process类
1,源码解析
-
class Process(): name: str daemon: bool pid: Optional[int] exitcode: Optional[int] authkey: bytes sentinel: int # TODO: set type of group to None def __init__(self, group: Any = ..., target: Optional[Callable] = ..., name: Optional[str] = ..., args: Iterable[Any] = ..., kwargs: Mapping[Any, Any] = ..., *, daemon: Optional[bool] = ...) -> None: ... def start(self) -> None: ... def run(self) -> None: ... def terminate(self) -> None: ... if sys.version_info >= (3, 7): def kill(self) -> None: ... def close(self) -> None: ... def is_alive(self) -> bool: ... def join(self, timeout: Optional[float] = ...) -> None: ...
2,类的参数
-
键 值 group None target 子进程执行任务的函数 name 子进程的名字 args target函数的参数,为一个元组,须有逗号 kwarg target函数的关键字参数,是一个字典
3,类的函数
-
函数名 作用 start() 启动进程并调用子进程的p.run run() 调用target指定的函数 terminate() 强制终止进程,使用前应杀掉子进程,并释放锁相关内容 is_alive() 判活 join() 等待子进程结束,可设置超时值
4,类的属性
-
属性 p.daemon 默认值是False,如果设为True,代表p 为后台运行的守护程序 p.name 进程的名称 p.pid 进程的pid p.exitcode 进程在运行时为None,-None表示信号N结束了 p.authkey 进程的身份验证键
创建进程的两种方式
1,第一种
-
在Windows环境下,开启进程必须在__name__ == ‘main’下面
-
from multiprocessing import Process import time def task(name): print(f'{name} is runing') time.sleep(3) print(f'{name} is done') if __name__ == '__main__': p = Process(target= task, args=('david',)) p.start() print('===主') ######################################### ===主 david is runing david is done
-
p.start(): 只是向操作系统发出一个开辟子进程的信号,然后就执行下一行,这个信号操作系统接收到之后,会从内存中开辟一个子进程空间,然后将主进程所有数据copy加载到子进程,然后调用CPU去执行
2,第二种
-
如果继承了Process类,创建进程时,类中方法必须定义成Process类的方法,类似于类的约束,但是如果没有定义成Process类的方法,程序不会报错,但是子进程没有创建成功
-
class MayProcess(Process): def __init__(self,name): super().__init__() self.name = name def run(self): print(f"{self.name}is run") time.sleep(2) print(f'{self.name} is dong') if __name__ == '__main__': p = MayProcess('david') p.start() print('===主') ######################################## ===主 davidis run david is dong
获取进程的pid
-
pid():内存中存在多个进程,每个进程的pid是唯一的
-
当进程结束,pid也随之消失
-
# from multiprocessing import Process # import time # import os # # def task(name): # print(f'子进程:{os.getpid()}') # print(f'主进程:{os.getppid()}') # # # if __name__ == '__main__': # p = Process(target=task,args=('world',)) # 创建一个进程对象 # p.start() # # print('==主开始') # print(f'====主{os.getpid()}') ########################################### ====主11228 子进程:5188 主进程:11228
验证进程之间的空间隔离
-
可变数据类型
-
物理隔离:内存不能共享内存你的数据(lock,队列除外)
-
lst = ['david',] def task(): lst.append('hello') print(f'子进程{lst}') if __name__ == '__main__': p = Process(target=task) # 创建一个进程对象 p.start() # print('==主开始') time.sleep(3) print(f'主:{lst}') ################################## 子进程['david', 'hello'] 主:['david']
-
不可变数据类型
-
from multiprocessing import Process import os import time name = 'david' def task(): global name name = 'hello' print(f'子进程{name}') if __name__ == '__main__': p = Process(target=task) # 创建一个进程对象 p.start() print('==主开始') time.sleep(3) print(f'主:{name}') #######################################
join
-
join():一种阻塞
-
join():实现子进程结束之后,在执行主进程
-
def task(sec): print('is run') time.sleep(sec) print('is gone') if __name__ == '__main__': start_time = time.time() # p1 = Process(target=task, args=(1,)) # p2 = Process(target=task, args=(2,)) # p3 = Process(target=task, args=(3,)) # p1.start() # p2.start() # p3.start() # p1.join() # p2.join() # p3.join() lst = [Process(target=task, args=(i,))for i in range(1, 4)] for i in lst: i.start() i.join() print(f'{time.time()-start_time}') ############################################## is run is run is run is gone is gone is gone 3.1298749446868896
-
def task(sec): print('is run') time.sleep(sec) print('is gone') if __name__ == '__main__': start_time = time.time() # p1 = Process(target=task, args=(1,)) # p2 = Process(target=task, args=(2,)) # p3 = Process(target=task, args=(3,)) # p1.start() # p2.start() # p3.start() # p1.join() # p2.join() # p3.join() lst = [Process(target=task, args=(i,))for i in range(1, 4)] for i in lst: i.start() for i in lst: i.join() print(f'{time.time()-start_time}') ############################################# is run is run is run is gone is gone is gone 3.1347217559814453
守护进程
-
守护进程会在主进程代码结束后就终止
-
守护进程内无法再开启子进程,否则抛出异常
-
# from multiprocessing import Process # import time # # def task(name): # print(f'{name} is running') # time.sleep(2) # print(f'{name} is gone') # # # # if __name__ == '__main__': # # 在windows环境下, 开启进程必须在 __name__ == '__main__' 下面 # p = Process(target=task,args=('david',)) # 创建一个进程对象 # p.daemon = True # 将p子进程设置成守护进程,只要主进程结束,守护进程马上结束. # p.start() # # p.daemon = True # 一定要在子进程开启之前设置 # time.sleep(1) # print('===主')
8,其他属性
-
# from multiprocessing import Process # import time # # def task(name): # print(f'{name} is running') # time.sleep(2) # print(f'{name} is gone') # # # # if __name__ == '__main__': # # 在windows环境下, 开启进程必须在 __name__ == '__main__' 下面 # # p = Process(target=task,args=('xx',)) # 创建一个进程对象 # p = Process(target=task,args=('xx',),name='alex') # p.start() # # time.sleep(1) # # p.terminate() # 杀死子进程 *** # # p.join() # *** # # time.sleep(0.5) # # print(p.is_alive()) # *** # # print(p.name) # p.name = 'sb' # print(p.name) # print('==主开始')
9,僵尸进程孤儿进程
1,僵尸进程
- 基于Unix环境(Linux,macOS)
- 定义:所有的子进程结束之后,在被主进程回收之前,都会进入僵尸进程状态
- 主进程需要等待子进程结束之后,主进程才结束
- 主进程时刻监测子进程的运行状态,当子进程结束之后,一段时间内,将子进程进行回收
- 为什么主进程不在子进程结束后马上对其回收呢
- 所有的子进程结束之后,会立马释放掉文件的操作系统,内存的大部分数据,但是会保留一些内容:进程号,结束时间,运行状态,等待主进程检测到,回收
- 主进程与子进程时异步关系,主进程无法马上捕获子进程什么时候结束
- 危害:如果父进程不对僵尸进程进行回收(wait,waitpid),产生大量的僵尸进程,这样就会占用内存,占用进程pid号
- 解决僵尸进程:
- 父进程产生大量的子进程,但是不回收僵尸进程,这样就造成大量的僵尸进程,占用进程pid,解决方法:就是直接杀死父进程,僵尸进程就变成了孤儿进程,交由init父进程回收
2,孤儿进程
- 父进程由于某种原因结束了,但是你的子进程还在运行中,这样你的这些字进程就成了孤儿进程, 你的父进程如果结束了,你的所有孤儿进程就会被init进程的回收,init就变成了你的父进程,对你进行回收