zoukankan      html  css  js  c++  java
  • multiprocessing

    进程概述:一个程序运行起来后,代码+用到的资源 称之为进程,它是操作系统分配资源的基本单元。

    进程创建类似线程,通过multiprocessing模块的Process类创建进程。类的括号内部也可以传入target=方法名和用args=()传入实参到要执行的方法内部,args也必须为元组。

      一个简单的多进程实例:

     1 import multiprocessing
     2 import time
     3 
     4 
     5 def test():
     6     for i in range(5):
     7         print('测试代码1', end='
    ')
     8         time.sleep(1)
     9 
    10 
    11 def dance():
    12     for i in range(5):
    13         print("测试代码2", end='
    ')
    14         time.sleep(1)
    15 
    16 
    17 if __name__ == '__main__':
    18     p1 = multiprocessing.Process(target=test)
    19     p2 = multiprocessing.Process(target=dance)
    20     p1.start()   # 启动线程,即让线程开始执行
    21     p2.start()

      运行结果:

     1 测试代码1
     2 测试代码2
     3 测试代码1
     4 测试代码2
     5 测试代码1
     6 测试代码2
     7 测试代码1
     8 测试代码2
     9 测试代码1
    10 测试代码2

    进程和线程的对比:

     功能:
            1.进程,能够完成多任务,比如 在一台电脑上能够同时运行多个QQ
            2.线程,能够完成多任务,比如 一个QQ中的多个聊天窗口
        定义的不同:
            1.进程是系统进行资源分配和调度的一个独立单位
            2.线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计时器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源
        区别:
            1.一个程序至少有一个进程,一个进程至少有一个线程
            2.线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高
            3.进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
            4.线程不能独立进行,必须依存在进程中
            5.可以将进程理解为工厂中的一条流水线,而其中的线程就是这个流水线上的工人
        优缺点:
            线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而线程正相反
    Queue队列:
      线程和线程之间如果需要共享资源、进行通信等,需要使用Queue类创建出队列变量,通过队列变量.put()方法向队列写入数据,通过队列变量.get()方法从队列拿取数据。队列变量.full()可以判断队列是够为满,队列变量.empty()可以判断队列是否为空,两个都返回布尔变量。
      当队列中数据存满(或为空)后继续向队列存数据(或取数据)会让程序处于等待状态不再向下执行。为了避免等待,可以使用队列变量.put_nowait()(或队列变量.get_nowait())让程序不用等待,执行结果会以异常的表达形式告知队列已满(或为空)。
      子进程间通信通过 multiprocessing.Queue() 创建,主进程和子进程间通信通过  multiprocessing.Manager().Queue() 创建(后面讲)
      子进程间通过队列通信实例:
     1 import multiprocessing
     2 
     3 
     4 def down_from_web(q):
     5     """从网站下载数据"""
     6     # 模拟从网上下载数据
     7     data = [11, 22, 33, 44]
     8     # 将数据写入队列
     9     for temp in data:
    10         q.put(temp)
    11 
    12     print("___下载器下载完成___")
    13 
    14 
    15 def analysis_data(q):
    16     """数据处理"""
    17     # 从队列获取数据
    18     waitting_analysis_data = list()
    19     while True:
    20         data = q.get()
    21         waitting_analysis_data.append(data)
    22         if q.empty():
    23             break
    24     # 模拟数量数据
    25     print(waitting_analysis_data)
    26 
    27 
    28 def main():
    29     # 1.创建一个Queue
    30     q = multiprocessing.Queue()
    31 
    32     # 2.创建多进程
    33     p1 = multiprocessing.Process(target=down_from_web, args=(q, ))
    34     p2 = multiprocessing.Process(target=analysis_data, args=(q, ))
    35     p1.start()
    36     p2.start()
    37 
    38 
    39 if __name__ == '__main__':
    40     main()

      运行结果:

    1 ___下载器下载完成___
    2 [11, 22, 33, 44]

    进程池Pool:

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

      初始化Pool时,可以指定一个最大进程数,当有新的请求提交到Pool中时,如果池还没满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到指定的最大值,那么该请求就会等待,直到池中有进程结束,才会用之前的进程来执行新的任务。

      进程池创建:  变量名 = multiprocessing.Pool(int)

      进程池案例:

     1 from multiprocessing import Pool
     2 import os, time, random
     3 
     4 
     5 def worker(msg):
     6     t_start = time.time()   # 获取开始时间
     7     print("%s开始执行,进程号为%d" % (msg, os.getpid()))
     8     # random.random()随机生成0~1之间的浮点数
     9     time.sleep(random.random()*2)
    10     t_stop = time.time()   # 获取执行结束时间
    11     print(msg, "执行完毕,耗时%.2f" % (t_stop-t_start))
    12 
    13 
    14 def main():
    15     # 定义一个线程池,最大线程数是3
    16     po = Pool(3)
    17     for i in range(0, 10):
    18         po.apply_async(worker, (i,))
    19 
    20     print("___start___")
    21     po.close()
    22     po.join()
    23     print("___end___")
    24 
    25 
    26 if __name__ == '__main__':
    27     main()

      运行效果:

     1 ___start___
     2 0开始执行,进程号为9744
     3 1开始执行,进程号为9172
     4 2开始执行,进程号为9836
     5 2 执行完毕,耗时0.10
     6 3开始执行,进程号为9836
     7 0 执行完毕,耗时0.59
     8 4开始执行,进程号为9744
     9 3 执行完毕,耗时0.46
    10 5开始执行,进程号为9836
    11 5 执行完毕,耗时0.22
    12 6开始执行,进程号为9836
    13 4 执行完毕,耗时0.36
    14 7开始执行,进程号为9744
    15 1 执行完毕,耗时1.89
    16 8开始执行,进程号为9172
    17 8 执行完毕,耗时0.06
    18 9开始执行,进程号为9172
    19 7 执行完毕,耗时1.03
    20 6 执行完毕,耗时1.36
    21 9 执行完毕,耗时1.77
    22 ___end___
    23 
    24 Process finished with exit code 0

      注意:如果是windows系统下创建进程池运行程序时,进程池相关代码应该放在if __name__ == '__main__'下面,否则会出现程序报错。详见:https://www.cnblogs.com/zuzhuangmengxiang/p/12663478.html

    综合案例(需要主进程和子进程间通信):

      简介:通过多进程拷贝一个文件夹下的所有文件到一个新的文件夹内,进程数做多为3个,程序开始后打印拷贝进度条。

      代码如下:

     1 import multiprocessing
     2 import os
     3 
     4 
     5 def copy_file(q, file_name, old_folder_name, new_folder_name):
     6     """读取并拷贝文件"""
     7     # print("模拟拷贝文件。。。%s" % file_name)
     8     old_f = open(old_folder_name+'/'+file_name, 'rb')
     9     content = old_f.read()
    10 
    11     new_f = open(new_folder_name+'/'+file_name, 'wb')
    12     new_f.write(content)
    13     # print("已拷贝文件 %s" % file_name)
    14     q.put(file_name)
    15 
    16 
    17 def main():
    18     # 1.获取用户要copy的文件夹的名字
    19     old_folder_name = input("请输入您要拷贝的文件夹名:")
    20 
    21     # 2.创建一个新的文件夹
    22     new_folder_name = old_folder_name+'[复件]'
    23     if not os.path.exists(new_folder_name):
    24         os.mkdir(new_folder_name)
    25 
    26     # 3.获取要copy的文件夹下所有的文件名称  listdir()
    27     file_names = os.listdir(old_folder_name)
    28 
    29     # 4.创建进程池
    30     po = multiprocessing.Pool(5)
    31 
    32     # 5.创建队列 —— multiprocessing.Manager().Queue()能够让主进程和子进程间进行通信
    33     q = multiprocessing.Manager().Queue()
    34 
    35     # 6.拷贝文件到新的文件夹内
    36     for file_name in file_names:
    37         po.apply_async(copy_file, (q, file_name, old_folder_name, new_folder_name))
    38     po.close()
    39     # po.join()
    40 
    41     # 用已下载个数/总个数显示进度条
    42     len_file = len(file_names)
    43     down_ok_num = 0
    44     while True:
    45         q.get()
    46         down_ok_num += 1
    47         print("已下载进度: %.02f %%" % (down_ok_num*100/len_file))
    48         if down_ok_num >= len_file:
    49             break
    50 
    51 
    52 if __name__ == '__main__':
    53     main()

      运行结果:

    1 请输入您要拷贝的文件夹名:test
    2 已下载进度: 0.58 %
    3 已下载进度: 1.17 %
    4 已下载进度: 1.75 %
    5 已下载进度: 2.34 %
    6 ......
    7 已下载进度: 100.00 %

      小改进:进度条要放在一行显示,只需百分比变动。print()内最左边加 表示行首显示,尾部加end=''表示每次循环打印的内容紧挨下次打印内容间。改进如下:

    1 print("
    已下载进度: %.02f %%" % (down_ok_num * 100 / len_file), end='')

      运行结果:

    1 请输入您要拷贝的文件夹名:test
    2 已下载进度: 100.00 %
  • 相关阅读:
    前端--HTML
    并发函数--线程
    并发编程--进程
    一个好用的网站,各种在线
    django Models与数据库关系
    流文件下载
    小白都能秒懂的各数据库在Django的配置
    关于django 内建缓存 信号 及自定义json的配置
    django批量创建数据
    关于drf的组件
  • 原文地址:https://www.cnblogs.com/zzmx0/p/12668223.html
Copyright © 2011-2022 走看看