进程池
为什么会有进程池的概念:
每次开启一个进程就要为其设立一个内存空间,这个需要设立内存空间需要时间;
其次进程过多,操作系统要不断在不同的进程之间进行调度,这个也需要时间。
所以进程的个数不能无限制增加。
这就需要涉及到进程池的概念。
进程池:
python中的进程池,先创建一个属于进程的池子;
这个池子指定能存放多个进程;
比如现在设立一个进程池:这个进程池中有5个个进程;当外部有50个任务需要执行的时候,首先这个5个任务对应五个进程开始执行,当每一个任务执行结束的时候,这个进程截至执行下一个任务,中间不会关闭,直至这50个任务最终执行完毕。
好处:相比50个任务同时执行,创建50个进程节省了内存空间,节省了50个进程创建所需要的时间,节省了操作系统在调度50个进程时所需要花费的时间;相比50个任务逐个执行,节省了时间。
信号量:是200个任务,产生200个进程,但是同一时间只能执行5个进程;
更高级的进程池:
进程池的个数有上限和下限
比如博客园这个网站,在白天上班时间访问的人多,晚上12点至凌晨6点访问的人少,也就是说访问量随着时间的变化有个波动。那么当访问少的时候,用进程池数量的下限,访问量大的时候用上限。这样能够减少操作系统的负担。
一般每个电脑的进程数量是处理器核数加1,比如四核的处理器最多是5个进程。
但是python里面没有这个高级进程池
经验:当处理需要5个进程以上的时候,就开启进程池
from multiprocessing import Pool def func(n): for i in range(10): print(n+1) if __name__=='__main__': pool=Pool(5)#进程池中设置五个进程 pool.map(func,range(100))#外部总共有100个进程
进阶,比较时间的差异
import time from multiprocessing import Pool,Process def func(n): for i in range(10): print(n+1) if __name__=='__main__': start1=time.time() pool=Pool(5)#进程池中设置五个进程 pool.map(func,range(100))#外部总共有100个进程 t1=time.time()-start1 #下面是用进程方法重新做一遍,来比较以下速度的差异 start2 = time.time() p_list=[] for i in range(100): p=Process(target=func,args=(i,)) p_list.append(p) p.start() for p in p_list: p.join() t2 = time.time() - start2 print(t1,t2) 得到t1=0.41053128242492676, t2=8.958720684051514
使用进程池,相比不使用进程池在时间上减少了20倍。
进程池的几种方法:
import time import os from multiprocessing import Pool,Process def func(n): print('start func%s'%n,os.getppid()) time.sleep(1) print('end func%s' % n, os.getppid()) if __name__=='__main__': p=Pool(5) for i in range(4): p.apply(func,args=(i,)) D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/sever.py start func0 9980 end func0 9980 start func1 9980 end func1 9980 start func2 9980 end func2 9980 start func3 9980 end func3 9980 Process finished with exit code 0
从以上结果可知,在执行过程中是start和end是同步执行的,那么这样想,如果是同步执行的话,那么进程池就失去了其原本的意义,那么如何实现异步执行。
import time import os from multiprocessing import Pool,Process def func(n): print('start func%s'%n,os.getppid()) time.sleep(1) print('end func%s' % n, os.getppid()) if __name__=='__main__': p=Pool(5) for i in range(8): p.apply_async(func,args=(i,))#实现进程池的异步执行 p.close()#结束进程池接收任务,阻止进程池中的进程再继续接收新的任务 p.join()#感知进程池中任务执行结束,因为进程池中的进程会在执行完一批任务后接着执行下一批任务,所以单纯使用join不能达到预期效果,必须在前面加上close,
用进程池实现socket聊天的效果
sever
import socket sk=socket.socket() sk.connect(('127.0.0.1',8080)) msg=sk.recv(1024).decode('utf-8') print(msg) msg1=input('>>>:').encode('utf-8') sk.send(msg1) sk.close()
这里代码自己感觉没有问题:但是运行后总是报错ConnectionRefusedError: [WinError 10061] 由于目标计算机积极拒绝,无法连接。以前从没有出现过,以后解决
clent端
import socket from multiprocessing import Pool def func(): conn.send(b'hello') print(conn.recv(1024).decode('utf-8')) conn.close() if __name__=='__main__': p=Pool(5) sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.listen() while 1: conn,addr=sk.accept() p.apply_async(func,args=(conn,)) sk.close() #实现接收和发送的异步
例子
from multiprocessing import Pool import time def func(i): time.sleep(0.5) return i*i if __name__=='__main__': p=Pool(5) for i in range(5): res=p.apply(func,args=(i,))#apply的结果就是func的返回值 print(res)
上面是appy的同步执行,在执行结果上,执行的结果是逐个被执行出来的
那么异步执行:
from multiprocessing import Pool import time def func(i): time.sleep(0.5) return i*i if __name__=='__main__': p=Pool(5) for i in range(10): res=p.apply_async(func,args=(i,))#apply的结果就是func的返回值 print(res.get())
在执行结果上,所有的执行结果还是一个个逐个出现,没有体现出异步执行应有的效果。其本质是计算的过程中使用了进程池,很快便执行完成;但是在res.get()时候发生了阻塞,所以导致结果是一个个逐渐出来,这时候就要将所有的进程放在列表中去执行。
from multiprocessing import Pool import time def func(i): time.sleep(0.5) return i*i if __name__=='__main__': p=Pool(5) res_list=[] for i in range(10): res=p.apply_async(func,args=(i,))#apply的结果就是func的返回值 res_list.append(res) for res in res_list: print(res.get())
使用map,
from multiprocessing import Pool import time def func(i): time.sleep(0.5) return i*i if __name__=='__main__': p=Pool(5) ret=p.map(func,range(10)) print(ret)
map和async的区别:
map是全部计算完以后再执行,
async是按照进程池中的进程数,分批执行,执行完一批输出一批。
map可以看作async的另一种形式,帮助我们简化了大量的操作。但是未来在实际使用中建议优先使用async
回调函数
from multiprocessing import Pool import os def func1(n): print('in func1:',os.getppid()) return n*n def func2(nn): print('in func2:', os.getppid()) print(nn) if __name__=='__main__': print('主进程:', os.getppid()) p=Pool(5) p.apply_async(func1,args=(10,),callback=func2) p.close() p.join() D:\anoconda\python.exe F:/python/python学习/人工智能/第一阶段day2/sever.py 主进程: 3432 in func1: 6156 in func2: 3432 100 Process finished with exit code 0
在上述代码中func2是回调函数,其是在主进程中被执行的。
且根据执行结果可知:func2执行的结果是func1执行的结果。
回调函数是在主进程中执行的
回调函数主要在爬虫中使用,用在爬虫的处理数据过程中。
例子:
import requests from multiprocessing import Pool def func1(url): response=requests.get(url) if response.status_code==200: return url,response.content.decode('utf-8') def func2(args): url,content=args print(url,len(content)) if __name__=='__main__': url_list=[ 'https://www.cnblogs.com/', 'http://www.baidu.com', 'https://www.sogou.com/', 'http://www.sohu.com/', ] p=Pool(5) for url in url_list: p.apply_async(func1,args=(url,),callback=func2) p.close() p.join()
在上面的例子中,主进程只是派发出去了url_list后没有其他任务,那么这样做不利于计算机工作效率的提升。将回调进程在主进程中执行,有助于提升计算机的效率。