• [Python]多任务编程--进程


    请添加图片描述


    前言

    系列文章目录
    [Python]目录
    视频及资料和课件
    链接:https://pan.baidu.com/s/1LCv_qyWslwB-MYw56fjbDg?pwd=1234
    提取码:1234



    多任务的介绍

    1 提问

    利用现学知识能够让两个函数或者方法同时执行吗?
    不能,因为之前所写的程序都是单任务的,也就是说一个函数或者方法执行完成另外一个函数或者方法才能执行,要想实现这种操作就需要使用多任务。

    多任务的最大好处是充分利用CPU资源,提高程序的执行效率

    2 多任务的概念

    多任务是指在同一时间内执行多个任务,例如: 现在电脑安装的操作系统都是多任务操作系统,可以同时运行着多个软件。

    多任务效果图:
    请添加图片描述

    3 多任务的执行方式

    1. 并发
    2. 并行

    3.1 并发

    在一段时间内交替去执行任务

    例如:
    对于单核cpu处理多任务,操作系统轮流让各个软件交替执行,假如:软件1执行0.01秒,切换到软件2,软件2执行0.01秒,再切换到软件3,执行0.01秒……这样反复执行下去。表面上看,每个软件都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像这些软件都在同时执行一样,这里需要注意单核cpu是并发的执行多任务的

    3.2 并行

    对于多核cpu处理多任务,操作系统会给cpu的每个内核安排一个执行的软件,多个内核是真正的一起执行软件。这里需要注意多核cpu是并行的执行多任务,始终有多个软件一起执行

    并行才是多个任务真正意义一起执行。

    对于多核CPU,如果执行的任务数小于CPU的核数,则并行执行;如果执行的任务数大于CPU的核数,则并发执行。

    进程

    1 进程的介绍

    Python程序中,想要实现多任务可以使用进程来完成,进程是实现多任务的一种方式

    2 进程的概念

    一个正在运行的程序或者软件就是一个进程,进程是操作系统进行资源分配的基本单位,也就是说每启动一个进程,操作系统都会给其分配一定的运行资源(内存资源)保证进程的运行。

    进程向操作系统索要内存资源,保证进程可以正常运行。真正在执行代码的是线程。

    例如,将社会看成是操作系统,社会中的各种资源尤其管理,现实生活中的公司可以理解成是一个进程,公司通过向社会获取资源,从而能够向员工提供办公资源(电脑、办公桌椅等),然而在公司中真正干活的是员工,员工可以理解成线程。

    注意:
    一个程序运行后至少有一个进程,一个进程默认有一个线程,进程里面可以创建多个线程,线程是依附在进程里面的,没有进程就没有线程

    3 进程的作用

    单进程效果图:
    请添加图片描述

    多进程效果图:
    请添加图片描述

    说明:
    多进程可以完成多任务,每个进程就好比一家独立的公司,每个公司都各自在运营,每个进程各自在运行,执行各自的任务。

    多进程的使用

    1 导入进程包

    #导入进程包
    import multiprocessing
    

    2 Process进程类的说明

    Process([group [, target [, name [, args [, kwargs]]]]])

    • group:指定进程组,目前只能使用None

    • 在这里插入图片描述

    • target:执行的目标任务名(需要执行的函数或方法的函数名或方法名)

    • name:进程名字(一般不用设置,每个进程会有默认的进程名)

    • args:以元组方式给执行任务传参

    • kwargs:以字典方式给执行任务传参

    Process创建的实例对象的常用方法

    • start():启动子进程
    • join():等待子进程执行结束
    • terminate():不管任务是否完成,立即终止子进程

    Process创建的实例对象的常用属性

    • name:当前进程的别名,默认为Process-N,N为从1开始递增的整数

    3 多进程完成多任务的代码

    # 导入进程包
    import multiprocessing
    import time
    
    
    # 唱歌任务
    def sing():
        for i in range(5):
            print('唱歌中...')
            time.sleep(0.2)
    
    
    # 跳舞任务
    def dance():
        for i in range(5):
            print('跳舞中...')
            time.sleep(0.2)
    
    
    # 判断当前运行的是否为主进程
    if __name__ == '__main__':
        # 创建跳舞的子进程
        dance_process = multiprocessing.Process(group=None, target=dance, name='dance')
        # 启动跳舞的子进程
        dance_process.start()
        # 主进程执行唱歌的任务
        sing()
    

    注意:
    执行的目标任务后面不能带(),否则相当于调用函数执行。

    进程的执行顺序是无序的,具体哪个进程先执行由操作系统调度决定。
    在这里插入图片描述

    获取进程编号

    1 获取进程编号的目的

    获取进程编号的目的是验证主进程和子进程的关系,可以得知子进程是由那个主进程创建出来的。

    获取进程编号的两种操作:
    (1)获取当前进程编号
    (2)获取当前父进程编号

    2 获取当前进程编号

    os.getpid() 表示获取当前进程编号

    # 获取当前进程对象
    multiprocessing.current_process()
    

    示例代码:

    # 导入进程包
    import multiprocessing
    import time
    # 导入获取进程编号相关的模块
    import os
    
    
    # 唱歌任务
    def sing():
        # 获取当前进程的id
        sing_process_id = os.getpid()
        # 获取当前进程对象
        sing_process_object = multiprocessing.current_process()
        print(sing_process_object, sing_process_id)
        for i in range(5):
            print('唱歌中...')
            time.sleep(0.2)
    
    
    # 跳舞任务
    def dance():
        # 获取当前进程的id
        dance_process_id = os.getpid()
        # 获取当前进程对象
        dance_process_object = multiprocessing.current_process()
        print(dance_process_object, dance_process_id)
        for i in range(5):
            print('跳舞中...')
            time.sleep(0.2)
    
    
    # 判断当前运行的是否为主进程
    if __name__ == '__main__':
        # 获取当前进程的id
        main_process_id = os.getpid()
        # 获取当前进程对象
        main_process_object = multiprocessing.current_process()
        print(main_process_object, main_process_id)
        # 创建跳舞的子进程
        dance_process = multiprocessing.Process(group=None, target=dance, name='dance')
        # 启动跳舞的子进程
        dance_process.start()
        # 主进程执行唱歌的任务
        sing()
    

    由于任务 sing 运行在主进程中,所以 sing 任务中获取的进程对象及进程编号与主进程相同。
    主进程的进程名为 MainProcess
    dance 任务的进程名为dance是由于创建进程时,由指定进程名。
    在这里插入图片描述

    3 获取当前父进程编号

    os.getppid() 表示获取当前父进程编号

    示例代码:

    # 导入进程包
    import multiprocessing
    import time
    # 导入获取进程编号相关的模块
    import os
    
    
    # 唱歌任务
    def sing():
        print('sing(): ')
        # 获取当前进程的id
        sing_process_id = os.getpid()
        # 获取当前进程对象
        sing_process_object = multiprocessing.current_process()
        print(sing_process_object, sing_process_id)
        print('父进程编号:', os.getppid())
        for i in range(5):
            print('唱歌中...')
            time.sleep(0.2)
    
    
    # 跳舞任务
    def dance():
        print('dance(): ')
        # 获取当前进程的id
        dance_process_id = os.getpid()
        # 获取当前进程对象
        dance_process_object = multiprocessing.current_process()
        print(dance_process_object, dance_process_id)
        print('父进程编号:', os.getppid())
        for i in range(5):
            print('跳舞中...')
            time.sleep(0.2)
    
    
    # 判断当前运行的是否为主进程
    if __name__ == '__main__':
        print('main: ')
        # 获取当前进程的id
        main_process_id = os.getpid()
        # 获取当前进程对象
        main_process_object = multiprocessing.current_process()
        print(main_process_object, main_process_id)
        # 创建跳舞的子进程
        dance_process = multiprocessing.Process(group=None, target=dance, name='dance')
        # 创建唱歌的子进程
        sing_process = multiprocessing.Process(target=sing, name='sing')
        # 启动子进程
        dance_process.start()
        sing_process.start()
    

    在这里插入图片描述

    4 扩展

    获取进程名

    语法:

    进程对象.name
    
    # 导入进程包
    import multiprocessing
    import time
    # 导入获取进程编号相关的模块
    import os
    
    
    # 唱歌任务
    def sing():
        print('sing(): ')
        # 获取当前进程对象
        sing_process_object = multiprocessing.current_process()
        print('进程名:', sing_process_object.name)
        for i in range(5):
            print('唱歌中...')
            time.sleep(0.2)
    
    
    # 判断当前运行的是否为主进程
    if __name__ == '__main__':
        print('main: ')
        # 获取当前进程对象
        main_process_object = multiprocessing.current_process()
        print('进程名:', main_process_object.name)
        # 创建唱歌的子进程
        sing_process = multiprocessing.Process(target=sing, name='sing')
        # 启动子进程
        sing_process.start()
    

    在这里插入图片描述

    根据进程编号强制杀死指定进程

    语法

    import os
    
    
    # 第二个参数为杀死进程的方式,9 -- 强制杀死进程
    os.kill(要杀死的进程编号, 9)
    
    # 导入进程包
    import multiprocessing
    import time
    # 导入获取进程编号相关的模块
    import os
    
    
    # 唱歌任务
    def sing():
        print('sing(): ')
        # 获取当前进程对象
        sing_process_object = multiprocessing.current_process()
        print('进程名:', sing_process_object.name)
        for i in range(5):
            print('唱歌中...')
            time.sleep(0.2)
            os.kill(os.getpid(), 9)
    
    
    # 判断当前运行的是否为主进程
    if __name__ == '__main__':
        print('main: ')
        # 获取当前进程对象
        main_process_object = multiprocessing.current_process()
        print('进程名:', main_process_object.name)
        # 创建唱歌的子进程
        sing_process = multiprocessing.Process(target=sing, name='sing')
        # 启动子进程
        sing_process.start()
    

    在这里插入图片描述

    进程执行带有参数的任务

    1. 进程执行带有参数的任务的介绍

    Process类执行任务并给任务传参数有两种方式:
    (1)args 表示以元组的方式给执行任务传参
    (2)kwargs 表示以字典方式给执行任务传参

    2. args参数的使用

    元组方式传参(args): 元组方式传参一定要和参数的顺序保持一致
    使用元组的方式进行传参,如果参数只有一个,元组的第一个逗号不能省略。

    import multiprocessing
    
    
    # 显示信息的任务
    def show_info(name, age):
        print(name, age)
    
    
    if __name__ == '__main__':
        # 创建子进程
        # 向任务传参使用 args 元组方式传参
        show_info_process = multiprocessing.Process(target=show_info, args=('zs', 20))
        # 启动子进程
        show_info_process.start()
    
    '
    运行

    在这里插入图片描述

    3. kwargs参数的使用

    字典方式传参(kwargs): 字典方式传参字典中的key一定要和参数名保持一致

    import multiprocessing
    
    
    # 显示信息的任务
    def show_info(name, age):
        print(name, age)
    
    
    if __name__ == '__main__':
        # 创建子进程
        # 向任务传参使用 args 元组方式传参
        show_info_process = multiprocessing.Process(target=show_info, kwargs={'name': 'zs', 'age': 29})
        # 启动子进程
        show_info_process.start()
    
    '
    运行

    在这里插入图片描述

    第一个参数的传递使用元组,第二个参数的传递使用字典:

    import multiprocessing
    
    
    # 显示信息的任务
    def show_info(name, age):
        print(name, age)
    
    
    if __name__ == '__main__':
        # 创建子进程
        # 向任务传参使用 args 元组方式传参
        show_info_process = multiprocessing.Process(target=show_info, args=('zs',), kwargs={'age': 29})
        # 启动子进程
        show_info_process.start()
    
    '
    运行

    在这里插入图片描述

    进程的注意点

    1. 进程的注意点介绍

    (1)进程之间不共享全局变量
    (2)主进程会等待所有的子进程执行结束再结束

    2. 进程之间不共享全局变量

    创建子进程会对主进程资源进行拷贝,也就是说子进程是主进程的一个副本,好比是一对双胞胎,之所以进程之间不共享全局变量,是因为操作的不是同一个进程里面的全局变量,是不同进程里面名字相同的全局变量。

    Windows下多进程程序,如果在主进程中没有if __name__ == '__main__':,会报错,因为创建子进程会对主进程资源进行拷贝,也就是说,子进程的代码与主进程一模一样,当主进程调用子进程时,由于子进程中存在直接执行新建子进程和调用子进程的代码,会出现无限递归调用,所以会报错。如果加上if __name__ == '__main__':判断,不是主进程就不会执行新建子进程和调用子进程的代码。

    import multiprocessing
    import time
    
    # 定义全局变量
    g_list = list()
    
    
    # 添加数据的任务
    def add_data():
        for i in range(5):
            g_list.append(i)
            print("add:", i)
            time.sleep(0.2)
    
        # 代码执行到此,说明数据添加完成
        print("add_data:", g_list)
    
    
    def read_data():
        print("read_data", g_list)
    
    
    if __name__ == '__main__':
        # 创建添加数据的子进程
        add_data_process = multiprocessing.Process(target=add_data)
        # 创建读取数据的子进程
        read_data_process = multiprocessing.Process(target=read_data)
    
        # 启动子进程执行对应的任务
        add_data_process.start()
        # 主进程等待添加数据的子进程执行完成以后程序再继续往下执行,读取数据
        add_data_process.join()
        read_data_process.start()
    
        print("main:", g_list)
    
        # 总结: 多进程之间不共享全局变量
    '
    运行

    在这里插入图片描述

    进程之间不共享全局变量的解释效果图:
    请添加图片描述

    3. 主进程会等待所有的子进程执行结束再结束

    假如我们现在创建一个子进程,这个子进程执行完大概需要2秒钟,现在让主进程执行0.5秒钟就退出程序,查看一下执行结果,示例代码如下:

    import multiprocessing
    import time
    
    
    # 定义进程所需要执行的任务
    def task():
        for i in range(10):
            print("任务执行中...")
            time.sleep(0.2)
    
    if __name__ == '__main__':
        # 创建子进程
        sub_process = multiprocessing.Process(target=task)
        sub_process.start()
    
        # 主进程延时0.5秒钟
        time.sleep(0.5)
        print("over")
        exit()
    
        # 总结: 主进程会等待所有的子进程执行完成以后程序再退出
    '
    运行

    在这里插入图片描述

    4. 主进程结束整个程序结束

    为了保证子进程能够正常的运行,主进程会等所有的子进程执行完成以后再销毁,设置守护主进程的目的是主进程退出子进程销毁,不让主进程再等待子进程去执行。

    1. 守护主进程:
      • 守护主进程就是主进程退出子进程销毁不再执行,主进程结束执行,整个程序结束执行。
      • 语法:子进程对象.daemon = True
    2. 子进程销毁:
      • 主进程要结束时候,将子进程销毁,子进程执行结束,整个程序结束。
      • 语法:子进程对象.terminate()

    4.1 设置守护主进程:

    在pycharm中可能执行不出效果,需要在终端执行代码。(Windows)
    在这里插入图片描述

    import multiprocessing
    import time
    
    
    # 定义进程所需要执行的任务
    def task():
        for i in range(10):
            print("任务执行中...")
            time.sleep(0.2)
    
    if __name__ == '__main__':
        # 创建子进程
        sub_process = multiprocessing.Process(target=task)
        # 设置守护主进程,主进程退出子进程直接销毁,子进程的生命周期依赖与主进程
        sub_process.daemon = True
        sub_process.start()
    
        time.sleep(0.5)
        print("over")
    
        # 总结: 主进程会等待所有的子进程执行完成以后程序再退出
        # 如果想要主进程退出子进程销毁,可以设置守护主进程或者在主进程退出之前让子进程销毁
    '
    运行

    在这里插入图片描述

    4.2 主进程结束时子进程销毁:

    在pycharm中可能执行不出效果,需要在终端执行代码。(Windows)
    在这里插入图片描述

    import multiprocessing
    import time
    
    
    # 定义进程所需要执行的任务
    def task():
        for i in range(10):
            print("任务执行中...")
            time.sleep(0.2)
    
    if __name__ == '__main__':
        # 创建子进程
        sub_process = multiprocessing.Process(target=task)
        sub_process.start()
    
        time.sleep(0.5)
        print("over")
        # 让子进程销毁
        sub_process.terminate()
        exit()
    
        # 总结: 主进程会等待所有的子进程执行完成以后程序再退出
        # 如果想要主进程退出子进程销毁,可以设置守护主进程或者在主进程退出之前让子进程销毁
    '
    运行

    在这里插入图片描述

  • 相关阅读:
    Lnmp架构之mysql数据库实战2
    判断一个数是否是回文(python和c#)
    【Ceres】Ceres学习笔记2 BA相关
    【机械臂视觉抓取从理论到实战】
    [树形dp]Crystalfly 2021年ICPC南京站H
    RabbitMQ——02
    自制python搜索小工具,比电脑自带的还要快
    0基础学习VR全景平台篇第110篇:源图像导入和镜头预设 - PTGui Pro教程
    五年后端开发,仅考这份面试题和答案,成功涨薪到30k!!!
    面试题—JAVA基础9.19/20
  • 原文地址:https://blog.csdn.net/m0_53022813/article/details/126839109