多进程

多进程

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就变成了你的父进程,对你进行回收
希望你眼眸有星辰,心中有山海,从此以梦为马,不负韶华
原文地址:https://www.cnblogs.com/daviddd/p/12034390.html