python学习笔记 day37 进程池

1. 进程池

进程池,只开指定数目的进程数(一般是CPU内核数+1)这样调度多个任务时,执行效率要比同时开多个进程执行效率要高很多(因为当同时开多个进程时,开进程是很占用资源的,时间都浪费在开进程上面了)

进程池方法-----p.map()

from multiprocessing import Pool
import time
import random
def func(i):
    time.sleep(random.random())  # 子进程执行时间有差异,然后可以看出进程池其实是异步的,每次并发五个进程,但是随着进程执行时间不一样所以顺序也不同
    print(i)

if __name__=="__main__":
    p=Pool(5)  # 进程池 只开五个进程
    p.map(func,range(20))  # 执行20个任务,但进程池只开5个进程
    p.close()  # 不允许在向进程池中发任务,一定要放在join()方法之前
    p.join()  # 主进程中等待子进程执行完(p.close() p.join()一定要写,普通的进程(Process)主进程会等待子进程执行完毕;
              # 但是进程池,主进程不会等子进程,如果不写这两句,主进程只管开5个进程,然后不等子进程执行完,就会结束了)

运行结果:

 我们可以比较一下,调度100个任务执行func函数,使用进程池和开100个进程执行效率上的差异:(开进程池的方法时间要少很多)

from multiprocessing import Process
from multiprocessing import Pool
import time

def func(i):  # 开的子进程执行func函数
    i+=1

if __name__=="__main__":
    p=Pool(5)  # 进程池中开5个进程(同一时间只能并发5个)
    start=time.time()  # 计算开进程池执行100个任务所需要的时间
    p.map(func,range(100))  # 调度100个任务,func是所开的进程需要执行的函数,第二个参数必须是iterable对象,把每一个元素都作为第一个参数func的参数传进去
    p.close()  # 不允许再向进程池中调度任务,也就是任务在上面一句都已经发送完了,p.close()一定要写在p.join()之前
    p.join()   # 主进程等待子进程执行完毕(进程池中开的子进程,主进程是不会等待的,所以一定要写上p.close() p.join())
    print("开进程池调度100个任务所使用的时间为:",time.time()-start)

    P=[] # 用来存放所开的进程,这样是为了计算开一百个进程所需要的时间,肯定得等100个进程执行完毕才可以计算;
        # 所以需要用到join()判断进程是否执行完毕,但是直接写p.join()就会变为同步的(一个进程执行完,才去开下一个进程)达不到并发的效果;
    start=time.time()  # 计算开100个进程所需要的时间
    for i in range(100):  # 第二种方法,调度100个任务,开100个进程来执行
        p=Process(target=func,args=(i,))
        p.start()
        P.append(p)
    [p.join() for p in P]  # 开的100个进程全都执行完毕
    print("开100个进程去执行func所需要的时间:",time.time()-start)

运行结果:

 所以执行同样的任务,开进程池比同时开多个进程效率要高很多;

2. 进程池的其他方法--p.apply()-----是一种同步提交任务的机制;

from multiprocessing import Pool
import time
import random

def func(i):
    time.sleep(random.random())  # 模拟每个进程执行时间是有差异的
    i+=1
    print(i)

if __name__=="__main__":
    p=Pool(5) # 进程池中开五个进程,实现5个并发的效果
    for i in range(10):
        p.apply(func,args=(i,))   # 进程池的apply方法是一种同步提交任务的机制,所以无法实现高并发(所以不用这种方法)
    p.close()   # 不允许再向进程池中添加任务
    p.join()   # 主进程等待子进程执行完毕

运行结果:

 3. 进程池的其他方法---p.apply_async()---是一种异步提交数据的机制

from multiprocessing import Pool
import time
import random

def func(i):
    time.sleep(random.random())  # 模拟每个进程执行时间是有差异的
    i+=1
    print(i)

if __name__=="__main__":
    p=Pool(5) # 进程池中开五个进程,实现5个并发的效果
    for i in range(10):
        p.apply_async(func,args=(i,))   # 进程池的apply_async()方法是一种异步提交任务的机制
    p.close()   # 不允许再向进程池中添加任务
    p.join()   # 主进程等待子进程执行完毕

运行结果:(最多5个并发)


 其实使用进程池还可以获得子进程的返回值

from multiprocessing import Pool
import time
import random

def func(i):
    time.sleep(1)
    i+=1
    print(i)
    return ("返回值:",i)

if __name__=="__main__":
    p=Pool(5)
    ret_lst=[]   # 存放每一个子进程执行func函数的返回值
    for i in range(20):   # 假设需要调度的任务有20个
        ret=p.apply_async(func,args=(i,))   # 拿到的就是子进程执行func()函数的返回值 return ("返回值:",i)
        # print(ret.get())  # 拿到的返回值,需要使用 get()方法才可以取到,这里不可以这样直接打印;
                       # 因为打印的话说明该进程必须得去执行func()函数才可以拿到返回值,所以必须得一个进程执行完毕,才可以去开下一个进程,这样就又变为同步了
                     # 就跟使用Process开多个进程,但是想在所有子进程执行完毕之后再执行主进程中的代码(比如统计所有子进程执行时间),就可以P.append(p),然后最后再同一[p.join() for p in P]实现高并发,异步编程的效果
        ret_lst.append(ret)

    p.close()   # 不允许再向主进程添加任务
    p.join()

    [print(ret.get()) for ret in ret_lst]  # 把子进程执行func()函数得到的返回值(存在列表ret_lst中的)最后统一取到

运行结果:

 

talk is cheap,show me the code
原文地址:https://www.cnblogs.com/xuanxuanlove/p/9784338.html