10月12日学习内容整理:队列JoinableQueue,共享内存Manager,进程池,回调函数
一、另一种队列:JoinableQueue类(multiprocessing模块)
1、作用:不用人为的去设置结束信号,可以自己发送信号
2、方法:
q=JoinableQueue() 得到对象
q.join() 等q结束,当q被取完就相当于结束
q.task_done() 队列的项目计数减1
3、举例
from multiprocessing import JoinableQueue,Process import time,random def producer(name,q,food): for i in range(1): time.sleep(random.randint(1,3)) res='%s%s' %(food,i) q.put(res) print('厨师 %s 生产了 %s' %(name,res)) q.join() #等q队列被取完结束,也就是消费者已经把数据全部处理完了 def consumer(name,q): while True: res=q.get() if res is None:break time.sleep(random.randint(1,3)) print('%s 吃了 %s' %(name,res)) q.task_done() #每取一次数据就将队列的项目计数减1,直至0就代表队列被取完 if __name__ == '__main__': q=JoinableQueue() p1=Process(target=producer,args=(1,q,'泔水')) p2=Process(target=producer,args=(2,q,'骨头')) p3=Process(target=producer,args=(3,q,'馒头')) c1=Process(target=consumer,args=('alex',q)) c2=Process(target=consumer,args=('wupeiqi',q)) c1.daemon=True #生产者等消费者全部处理完数据后就结束了,但是此时消费者因为还在等着取队列中的数据而没有结束,而 c2.daemon=True #消费者程序应该也要结束,所以就设置为守护进程,当主程序结束后消费者也随之结束 p1.start() p2.start() p3.start() c1.start() c2.start() p1.join() p2.join() p3.join() #生产者全部结束意味着消费者已经全部处理完数据了,消费者可以结束了
二、共享内存:Manager类(multiprocessing模块)
1、方法
m = Manager() 得到对象
d = m.dict( 字典 ) 所有的进程都可以操作这个字典,这个字典就存在内存中
m.list(列表) 也可以创建列表
2、举例
from multiprocessing import Manager,Process,Lock def work(d,lock): with lock: temp=d['count'] d['count']=temp-1 if __name__ == '__main__': m=Manager() d=m.dict({"count":100}) lock=Lock() p_l=[] for i in range(100): p=Process(target=work,args=(d,lock)) p_l.append(p) p.start() for obj in p_l: #为了等全部的进程结束后,打印最终处理完后的字典数据 obj.join() print(d)
三、进程池
1、应用背景:
》》因为cpu核数和系统资源的限制,我们不能无限制的开启进程,而人为的去限制进程个数又很麻烦,所以就需要一个容器来控制进程的数量,这就是进程池
2、特点:
》》用来控制进程的个数,当有任务接受时,进程池就分配进程来执行任务,若进程数已满,则额外的任务就需要等待直至有空闲的进程来执行
》》进程池里的进程号是不会随着任务的不同而发生改变的
3、缺点:
》》进程池的进程数取决于核数,显然数目很小,处理数目很庞大的并发量时很明显是不行的,效率会非常低
4、方法:Pool类(multiprocessing模块)
》p= Pool(4) 创建对象,4为进程池里的最大进程数
》同步调用方式:p.apply(函数名,args=(参数)/kwds={参数}) 进程池接收到任务自动开启进程执行,并且等待进程结束,可以立刻拿到结果
》异步调用方式:obj=p.apply_async(函数名,args=(参数)/kwds={参数})只把任务提交给进程池,不用等待进程结束,但是没法立刻得到结果
当进程池已满时,还会向进程池提交任务,但此时任务是不会被执行的,当进程池中有空闲进程时,被阻塞的进程就会被立刻执行
》p.close()关闭进程池,不再接受任务请求
》p.join() 等进程中的任务全部结束,必须跟close连用
》obj.get() 取出进程运行的结果,obj是每一个任务的对象
5、举例:
from multiprocessing import Pool import os,time,random def work(n): print('%s is working' %os.getpid()) time.sleep(random.randint(1,3)) return n**2 if __name__ == '__main__': p=Pool(4) objs=[] for i in range(10): # 同步调用:提交完任务后,在原地等待任务结束,一旦结束可以立刻拿到结果 # res=p.apply(work,args=(i,)) # print(res) #可以马上拿到结果 # 异步调用:提交完任务后,不会在原地等待任务结束,会继续提交下一次任务,等到所有任务都结束后,才get结果 obj=p.apply_async(work,args=(i,)) objs.append(obj) p.close() p.join() #必须等到所有进程结束后才能得到结果 for obj in objs: print(obj.get()) print('主')
——》补充:
》阻塞:正在运行的进程遇到IO操作则进入阻塞状态
》非阻塞:可能是运行态或者就绪态
》同步调用:提交任务后在原地等待任务结束
》异步调用:提交完任务不会等结束,而是继续提交下一个任务,但是不能立刻拿到结果
》集中式:生产者和消费者还有第三方(队列)都在一台机器上,可以处理少数的并发量
》分布式:因为硬件和cpu的限制,当并发量很庞大时一台显然满足不了,就可以用多台计算机运行生产者和多台计算机运行消费者来满足庞大并发量的需求,还有专门运行第三方(队列)软件的计算机服务器,而这台服务器就是整个网络的核心,性能要求高。
四、回调函数
1、应用背景:进程池中任何一个任务一旦处理完了,就立即告知主进程:我好了额,你可以处理我的结果了。主进程则调用一个函数去处理该结果,该函数即回调函数,意思就是进程池中某个进程的运行结果还要调给主程序的函数继续处理
2、方法:
》obj=p.apply_async(函数名1,args=(参数),callback=函数名2) callback就是回调函数
3、特点:
》回调函数的参数即是函数名1的执行返回结果
》主进程负责干回调函数的活
》通常应用的话回调函数的执行时间要比函数名1的执行时间少很多
4、举例(爬虫应用很多)
from multiprocessing import Pool,Process import requests import os import time,random def get(url): print('%s GET %s' %(os.getpid(),url)) response=requests.get(url) time.sleep(random.randint(1,3)) if response.status_code == 200: print('%s DONE %s' % (os.getpid(), url)) return {'url':url,'text':response.text} def parse(dic): #回调函数 print('%s PARSE %s' %(os.getpid(),dic['url'])) time.sleep(1) res='%s:%s\n' %(dic['url'],len(dic['text'])) with open('db.txt','a') as f: f.write(res) if __name__ == '__main__': urls=[ 'https://www.baidu.com', 'https://www.python.org', 'https://www.openstack.org', 'https://help.github.com/', 'http://www.sina.com.cn/' ] p=Pool(2) #进程池 start_time=time.time() objs=[] for url in urls: obj=p.apply_async(get,args=(url,),callback=parse) #主进程负责干回调函数的活 objs.append(obj) #将对象先储存起来 p.close() p.join() #等进程池中的所有进程结束 print('主',(time.time()-start_time))