python多进程讲解

一、第一个demo

  1. 进程:一个程序运行起来后,代码+用到的资源 称为进程,他是操作系统非配资源的基本单元。线程完成的多任务,进程也可以
  2. 进程在python中使用multiprocessing模块中的Process来定义使用。
  3. 举例:
    """多任务进程"""
    import multiprocessing
    import time
    
    def test1():
        while True:
            print('---test1----')
            time.sleep(1)
    
    
    def test2():
        while True:
            print('----test2----')
            time.sleep(1)
    
    
    def main():
        """进程"""
        p1 = multiprocessing.Process(target=test1)
        p2 = multiprocessing.Process(target=test2)
        p1.start()
        p2.start()
    
    
    if __name__ == '__main__':
        main()
    
    运行结果:
    在这里插入图片描述

二、队列Queue

进程Queue队列:(解耦耦合性比较低) (完成数据间内容的共享)
  1. 导入:from multiprocessing import Queue
  1. 创建并规定里面最多存多少:q = Queue(X) X:是最多存多少数据
  2. 放数据: q.put() 取数据: q.get() get()如果空了会堵塞 put()如果满了也会堵塞
  3. 判断是否满了: q.full() (满的时候为true) 判断是否空了: q.empty() (空的时候为true) 返回值是True:为满/空
  4. 如果没有数据这个q.get_nowait()会抛出异常
  5. 数据类型任意
实例:

模拟在网上下载东西

"""多任务 --进程 --Queue队列 进程间数据共享"""
import multiprocessing
from multiprocessing import Queue


def download_form_web(q):
    """模拟从网站下载东西"""
    data = [11, 22, 33, 44]
    # 向队列中写数据
    for temp in data:
        q.put(temp)
    # 输出一个写成功的提示
    print("----下载器已经下载完,已将数据写入队列----")


def analysis_data(q):
    """从队列中读取数据"""
    waitting = list()
    # 从队列中读取数据
    while True:
        data = q.get()
        waitting.append(data)
        # 如果是空了则退出 empty:空的时候为true
        if q.empty():
            break
    print(waitting)


def main():
    """Queue队列,进程间通信"""
    # 1. 创建队列 不写参数,根据计算机的硬件分配空间
    q = Queue()
    # 2.创建多个进程将队列作为实参传入
    p1 = multiprocessing.Process(target=download_form_web, args=(q, ))
    p2 = multiprocessing.Process(target=analysis_data, args=(q, ))
    # 启动进程
    p1.start()
    p2.start()


if __name__ == "__main__":
    main()

三、进程池Pool

  1. 当需要创建子进程数量不多时,可以直接利用multiprocessing中的Process动态生成多个进程,如果是上百甚至上千目标,手动的去创建进程的工作量巨大,此时就可以用multiprocessing模块提供的Pool方法。

  2. 初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果进程池没有满则会创建一个新的进程用来执行该请求;如果进程池满的话则需要等待,直到进程池中有进程结束,才会用之前的进程来执行新的任务。

  3. 初始化进程池:Pool(x):x为最大的进程数量

  4. 创建并调用目标函数:zpply_async(要调用的目标函数, (传递给目标的参数元组, ))

  5. join():等待进程池中的所有进程执行完毕(要放在close()后面)

  6. close():关闭进程池–>进程池不会在接受新的请求

  7. os.getpid(): 查看进程号

  8. 实例:

    """进程池"""
    from multiprocessing import Pool
    import os, time, random
    
    
    def worker(msg):
        """进程池工作"""
        t_start = time.time()
        print("%s开始执行, 进程号为:%d" % (msg, os.getpid()))
        # random.random() 随机生成0-1之间的浮点数
        time.sleep(random.random()*2)
        t_stop = time.time()
        print(msg, "执行完毕,耗时%0.2f" % (t_stop - t_start))
    
    def main():
        po = Pool(3)  # 定义一个进程池,最大进程数为3
        for i in range(0,10):
            print(f"---------{i}---------")
            # Pool().apply_async(要调用的目标函数, (传递给目标的参数元组, ))
            # 每次循环将会用空闲处理啊的子进程去调用目标
            po.apply_async(worker,(i, ))
    
    
        print("----start----")
        po.close()  # 关闭进程池, 关闭后po将不再接受新的请求
        po.join()  # 等待po中所有子进程执行完成, 必须放在close语句后面
        print("----end----")
    
    
    if __name__ == "__main__":
        main()
    

    执行结果:
    在这里插入图片描述

四、案例:文件复制

  1. 使用进程池完成基本的功能

    """多进程案例 -- 文件夹复制"""
    import os
    import multiprocessing
    
    
    def copy_folder(file_name, old_folder_name, new_folder_name):
        """复制文件"""
        print("复制%s目录下的%s到%s下" % (old_folder_name, file_name, new_folder_name))
        # 读文件
        old_file = open(old_folder_name + '/' + file_name, 'rb')
        count = old_file.read()
        old_file.close()
        # 写文件
        new_file = open(new_folder_name + '/' + file_name, 'wb')
        new_file.write(count)
        new_file.close()
    
    
    def main():
        """多进程文件夹的复制"""
        # 获取要复制的文件夹名字
        old_folder_name = input("请输入要复制的文件夹名:")
        # 新文件夹的名字+创建
        new_folder_name = old_folder_name + "_附件"
        print(new_folder_name)
        try:
            # 如果已经创建会出现异常
            os.mkdir(new_folder_name)
        except:
            pass
        # 获取文件夹中的文件 os.listdir()
        file_names = os.listdir(old_folder_name)
        # 复制文件
        # 创建进程池
        po = multiprocessing.Pool()
        for file_name in file_names:
            # 向进程池中添加任务
            po.apply_async(copy_folder,args=(file_name, old_folder_name, new_folder_name))
    
        po.close()
        po.join()
    
    
    
    if __name__ == "__main__":
        main()
    

    运行结果:
    在这里插入图片描述

  2. 使用Queue队列通信添加进度条

    """多进程案例 -- 文件夹复制 -- Queue队列通信 进度条"""
    import os
    import multiprocessing
    
    
    def copy_folder(q, file_name, old_folder_name, new_folder_name):
        """复制文件"""
        # print("复制%s目录下的%s到%s下" % (old_folder_name, file_name, new_folder_name))
        # 读文件
        old_file = open(old_folder_name + '/' + file_name, 'rb')
        # 写文件
        new_file = open(new_folder_name + '/' + file_name, 'wb')
        while True:
            count = old_file.read(1024)
            if count:
                new_file.write(count)
            else:
                break
        old_file.close()
        new_file.close()
        # 写完然后向队列中传消息
        q.put(file_name)
    
    
    def main():
        """多进程文件夹的复制"""
        # 获取要复制的文件夹名字
        old_folder_name = input("请输入要复制的文件夹名:")
        # 新文件夹的名字+创建
        new_folder_name = old_folder_name + "_附件"
        # print(new_folder_name)
        try:
            # 如果已经创建会出现异常
            os.mkdir(new_folder_name)
        except:
            pass
        # 获取文件夹中的文件 os.listdir()
        file_names = os.listdir(old_folder_name)
        # 复制文件
        # 创建队列
        q = multiprocessing.Manager().Queue()
        # 创建进程池
        po = multiprocessing.Pool()
        for file_name in file_names:
            # 向进程池中添加任务
            po.apply_async(copy_folder,args=(q, file_name, old_folder_name, new_folder_name))
    
        po.close()
        # po.join()
        # 总共的文件数
        file_num = len(file_names)
        file_num_ok = 0
        # 显示进度
        while True:
            file_name_ok = q.get()
            # print("%s文件已经拷贝完成" % file_name_ok)
            file_num_ok +=1
            print("\r拷贝完成的进度:%.2f %%" % (file_num_ok*100 / file_num), end="")
            if file_num_ok >= file_num:
                break
    
    
    if __name__ == "__main__":
        main()
    

    运行结果:
    在这里插入图片描述

五、总结

进程和线程的区别:
定义不同:
  1. 进程是系统进行资源分配和调度的一个独立单位
  1. 线程是进程中的一个实体,是cpu调度和分派的基本单位
  1. 一个进程中一定包括一个线程
区别:
  1. 一个一个程序至少有一个进程,一个进程至少有一个线程
  1. 线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发姓高
  1. 进程执行过程中拥有独立的内存单元,而多线程共享内存,从而极大地提高了程序的运行效率
  1. 线程不能单独运行,必须依存在进程中

版权声明:本文为weixin_43844838原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。