• 45_并发编程-进程池


    一、为什么引入进程池
        
      在程序实际处理问题过程中,忙时会有成千上万的任务需要被执行,闲时可能只有零星任务。那么在成千上万个任务需要被执行的时候,我们就需要去创建成千上万个进程么?首先,创建进程需要消耗时间,销毁进程(空间,变量,文件信息等等的内容)也需要消耗时间。第二即便开启了成千上万的进程,操作系统也不能让他们同时执行,维护一个很大的进程列表的同时,调度的时候,还需要进行切换并且记录每个进程的执行节点,也就是记录上下文(各种变量等等乱七八糟的东西,虽然你看不到,但是操作系统都要做),这样反而会影响程序的效率。因此我们不能无限制的根据任务开启或者结束进程。就看我们上面的一些代码例子,你会发现有些程序是不是执行的时候比较慢才出结果,就是这个原因,那么我们要怎么做呢?
     
      在这里,要给大家介绍一个进程池的概念,定义一个池子,在里面放上固定数量的进程,有需求来了,就拿一个池中的进程来处理任务,等到处理完毕,进程并不关闭,而是将进程再放回进程池中继续等待任务。如果有很多任务需要执行,池中的进程数量不够,任务就要等待之前的进程执行任务完毕归来,拿到空闲进程才能继续执行。也就是说,池中进程的数量是固定的,那么同一时间最多有固定数量的进程在运行。这样不会增加操作系统的调度难度,还节省了开闭进程的时间,也一定程度上能够实现并发效果。
     
    二、multiprocessing 引入Pool 模块
    1 1、语法格式:Pool([numprocess [,initializer [, initargs]]]):创建进程池
    2         numprocess:要创建的进程数,如果省略,将默认使用cpu_count()的值
    3         initializer:是每个工作进程启动时要执行的可调用对象,默认为None
    4         initargs:是要传给initializer的参数组
    语法格式

       1、创建进程池的类:如果指定numprocess为3,则进程池会从无到有创建三个进程,然后自始至终使用这三个进程去执行所有任务(高级一些的进程池可以根据你的并发量,搞成动态增加或减少进程池中的进程数量的操作),不会开启其他进程,提高操作系统效率,减少空间的占用等。numprocess不行会选择cpu分配,1  -> processes = os.cpu_count() or 1

     
      2、主要方法
     1 <1> map()  - 并且自带close和join,一般约定俗成的是进程池中的进程数量为CPU的数量,工作中要看具体情况来考量。
     2 
     3 # 进程池与进程的时间比较
     4 
     5 import time
     6 from multiprocessing import Process, Pool
     7 
     8 def func(n):
     9     print(n)
    10 
    11 if __name__ == '__main__':
    12 
    13     # 进程池运行时间
    14     time_pool_start = time.time()
    15     pool =Pool(4)   # 进程池中只放4个进程
    16     pool.map(func, range(100))      # 使用map方法实现,map实现的是异步的。map参数可迭代 range(100),把可迭代的元素放入func中执行
    17     time_pool_end = time.time()
    18     p_dif = time_pool_end - time_pool_start
    19 
    20     # 进程运行时间
    21     time_pro_start = time.time()
    22     lst = []
    23     for i in range(100):
    24         p = Process(target=func, args=(i,))
    25         p.start()
    26         lst.append(p)
    27     [obj.join() for obj in lst]
    28     time_pro_end = time.time()
    29     p_dif_pro = time_pro_end - time_pro_start
    30 
    31     print("进程池>>",p_dif)
    32     print("进程>>", p_dif_pro)
    map方法
     1 <2> apply()       -        apply(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果。同步执行,一个一个出,它会等待任务的执行结果
     2                    需要强调的是:此操作并不会在所有池工作进程中并执行func函数。如果要通过不同参数并发地执行func函数,必须从不同线程调用p.apply()函数或者使用
     3 
     4 import time
     5 from multiprocessing import Process,Pool
     6 
     7 def fun(i):
     8     time.sleep(0.5)
     9     # print(i)
    10     return i**2
    11 
    12 if __name__ == '__main__':
    13     p = Pool(4)
    14     for i in range(10):
    15         res = p.apply(fun,args=(i,))  #同步执行的方法,他会等待你的任务的返回结果,
    16         print(res)
    apply方法
     1 <3> apply_async()    -   apply_async(func [, args [, kwargs]]):在一个池工作进程中执行func(*args,**kwargs),然后返回结果; 异步的
     2                         此方法的结果是AsyncResult类的实例,callback是可调用对象,接收输入参数。当func的结果变为可用时,将理解传递给callback。callback禁止执行任何阻塞操作,否则将接收其他异步操作中的结果。
     3 
     4 import time
     5 from multiprocessing import Pool
     6 
     7 def func(n):
     8     time.sleep(0.5)
     9     return n**2
    10 
    11 if __name__ == '__main__':
    12 
    13     p = Pool(4)
    14     lst = []    # 设置一个空列表
    15     for i in range(10):
    16         ret = p.apply_async(func, args=(i,))  # 异步步执行,获取返回值
    17         lst.append(ret)     # 异步的,返回值放一个列表
    18     time.sleep(1)
    19     for el in lst:  # 循环列表
    20         print(el.get()) # 获取异步结果,因为get()一次只能取一个值,使用进程池取数据,每4个一组一打
    21 
    22 ----------------------------------------------------------------------------------------
    23 import time
    24 from multiprocessing import Pool
    25 
    26 def func(n):
    27     time.sleep(0.5)
    28     print(n)
    29     return n**2
    30 
    31 if __name__ == '__main__':
    32 
    33     p = Pool(4)
    34     lst = []    # 设置一个空列表
    35     for i in range(10):
    36         ret = p.apply_async(func, args=(i,))  # 异步步执行,获取返回值,4个一组
    37         lst.append(ret)     # 异步的每4个放一个列表
    38 
    39     p.close() # 不是关闭进程池,而是不允许再有其他任务来使用进程池
    40     p.join()    # 这是感知进程池中任务的方法,等待进程池的任务全部执行完
    41 
    42     for el in lst:  # 循环列表,这样把进程池关上,不允许别的任务用,就会全部打印出来
    43         print(el.get()) # 获取异步结果,因为get()一次只能取一个值
    44     print('主进程')
    45 
    46 ----------------------------------------------------------------------------
    47 Close:由于我们想看看进程池中现有的任务是否全部运行完毕,或者说我想获取进程池中当前所有任务的结果,我们需要等待全部任务执行完毕,但是可能会有新的任务进来,所以没办法确定我现有的任务是多少,我要等他完,但是有新任务一直进来就完不了,所有我们要锁定进程池,不让其他任务进来.使用close
    apply_async方法
      
      3、回调函数 - callback
      需要回调函数的场景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我的结果了。主进程则调用一个函数去处理该结果,该函数即回调函数,这是进程池特有的,普通进程没有这个机制,但是我们也可以通过进程通信来拿到返回值,进程池的这个回调也是进程通信的机制完成的。
      我们可以把耗时间(阻塞)的任务放到进程池中,然后指定回调函数(主进程负责执行),这样主进程在执行回调函数时就省去了I/O的过程,直接拿到的是任务的结果
     1 import os
     2 from multiprocessing import Pool
     3 
     4 def func1(n):
     5     print('func1>>',os.getpid())
     6     # print('func1')
     7     return n*n
     8 
     9 
    10 def func2(nn):
    11     print('func2>>',os.getpid())
    12     # print('func2')
    13     print(nn)
    14     # import time
    15     # time.sleep(0.5)
    16 if __name__ == '__main__':
    17     print('主进程:',os.getpid())
    18     p = Pool(4)
    19     p.apply_async(func1,args=(10,),callback=func2)    # func1->子进程运行; func2->主进程运行,把func1返回的值给func2去执行其他操作
    20     p.close()
    21     p.join()
    callback
  • 相关阅读:
    Flink 架构和拓扑概览
    Flink 如何背压
    流式计算的时间模型
    流式计算的背压问题
    大数据流式计算容错方案演进之路
    Flink 任务提交
    Flink wordCount
    线性回归和逻辑回归的区别
    Nginx反向代理后配置404页面
    Httpclient 实现带参文件上传
  • 原文地址:https://www.cnblogs.com/hq82/p/9851654.html
Copyright © 2020-2023  润新知