一,进程之间通信,简称:IPC:inter-process-communication
概念:
1.多个进程之间有一些固定的通信内容(一些信号)
2.实现方式:socket基于文件家族通信
3.进程之间虽然内存不共享,但是可以通信的
Lock,Semaphore,Event 都会进行进程之间的通信,只不过这些通信的内容我们不能改变
进程队列:先进先出 ****
import queue from multiprocessing import Queue q = Queue() #若不设置,则对列长度不限制 q = Queue(2) #设置队列长度,队列满了会阻塞 #put()与put_nowait() q.put() #若队列已满,数据进不去,并阻塞 q.put_nowait(3) #若队列已满,进不去队列,则报错,并丢弃本次数据 #get()与get_nowait() q.get() #若队列为空,或取值不存在,则阻塞 q.get_nowait(3) #如果get队列,队列为空,或取值不存在,则报错 #empty()与full() 慎用,在多进程对一个队列操作时,不准! q.empty():查看队列是否为空 q.full():查看队列是否为满 #nowait用法 try: print(q.get_nowait()) except queue.Empty: print('empty')
#例:主进程与子进程通过进程队列传递数据 from mulitprocessing import Process,Queue def consume(q): print(q.get()) #取值 q.put('abc') #存值 if __name__ == '__main__': q = Queue() p = Process(target=consume,args=(q,)) p.start() q.put({'123':123}) #存值 p.join() #等待子进程执行完成 print(q.get()) #取值
进程队列应用:生产者消费者模型
#做饭吃饭的例子 import time import random from multiprocessing import Queue,Process def consumer(q,name): #处理数据/消费者 while True: food = q.get() if food is None:break print('%s吃了一个%s'%(name,food)) time.sleep(random.uniform(0.5,1)) def producer(q,name,food): #获取数据/生成者 for i in range(10): time.sleep(random.uniform(0.3,0.8)) print('%s生产了%s%s'%(name,food,i)) q.put(food+str(i)) if __name__ == '__main__': q = Queue() c1 = Process(target=consumer,args=(q,'alex')) c2 = Process(target=consumer,args=(q,'wusir')) # c1.daemon = True c1.start() c2.start() p1 = Process(target=producer,args=(q,'taibai','饭')) p2 = Process(target=producer,args=(q,'egon','菜')) p1.start() p2.start() p1.join() p2.join() q.put(None) #有几个consumer就需要在最后放几个none q.put(None)
进程队列之joinablequeue模块:
joinablequeue是一个类,包含put(),get(),task_done(),join()方法
import time import random from multiprocessing import Queue,Process from multiprocessing import JoinableQueue def consumer(q,name): #处理数据 while True: food = q.get() print('%s吃了一个%s'%(name,food)) time.sleep(random.uniform(0.5,1)) q.task_done() #向q.join()发送一次信号,证明一个数据已经被取走了 def producer(q,name,food): #获取数据 for i in range(10): time.sleep(random.uniform(0.3,0.8)) print('%s生成%s%s'%(name,food,i)) q.put(food+str(i)) #生产数据并存值到进程队列 if __name__ == '__main__': q = JoinableQueue() c1 = Process(target=consumer,args=(q,'alex')) c1.daemon = True #消费者进程设置为守护进程 c2 = Process(target=consumer,args=(q,'wusir')) c2.daemon = True #消费者进程设置为守护进程 c1.start() c2.start() p1 = Process(target=producer,args=(q,'taibai','饭')) p2 = Process(target=producer,args=(q,'egon','菜')) p1.start() p2.start() p1.join() #生产者进程执行完成前阻塞 p2.join() #生产者进程执行完成前阻塞 q.join() #生产完毕后,使用此方法进行阻塞,直到队列中所有项目均被处理(即:ask_done的值为0) #当队列中的数据被处理完后,结束阻塞,消费者程序随着主进程代码完成而结束
二,管道 **
1.队列是基于管道实现
2.管道是基于socket、pickle实现的
3.管道与队列的区别
队列:进程之间数据安全,严格控制数据进出(管道+锁实现简便的IPC机制)
管道:进程之间数据是不安全的,且存取数据复制
4.pipe的端口管理:
pipe的端口管理不会随着某一个进程的关闭而关闭,pipe的端口属于系统资源,需要手动释放
操作系统来管理进程对这个端口的使用
操作系统管理pipe端口,每当手动关闭一个端口计算-1,直到所有可以传值的端口关闭,剩余一个端口的时候,系统报错,我们可以利用异常处理对报错信息进行处理并关闭子进程。
from multiprocessing import Pipe left,right = Pipe() left.send(12345) #管道中的存值与取值,send,recv通过pickel和socket实现 print(right.recv())
from multiprocessing import Pipe,Process def consumer(left,right): left.close() #关闭子进程right端口 while True: try: print(right.recv()) #当子进程取不到值时,并可以Pipe可以接受传值的端口都被关闭时,则报错 except EOFError: #利用报错,进行异常处理,以结束子进程 break if __name__ == '__main__': left, right = Pipe() Process(target=consumer, args=(left, right)).start() right.close() #关闭主进程right端口 for i in range(10): left.send('饭菜%s'%i) left.close() #当数据存取完成是,关闭left端口,否则子程序的pipe端口管理会认为,数据还没有存取完毕,子进程会进入阻塞状态
三,进程池 *****
1:为什么要有进程池?
开启过多的进程并不能提高你的效率,反而会降低效率
2.进程任务的概念:
计算密集型:充分占用cpu,多进程可以充分利用多核
适合开启多进程,但是不适合开启很多多进程
IO密集型:大部分时间都在阻塞队列,而不是在运行状态中
根本不适合开启多进程
3.进程池的创建
#实例化 传参数(进程的个数) 进程个数默认为cpu个数 或 为1,一般为os.cpu_count() + 1 p = Pool() #提交任务 #同步提交func到一个子进程中执行 ret = p.apply(func,args=(i,)) #返回值:子进程对应函数的返回值 #一个一个顺序执行,并没有任何并发效果 #异步提交func到一个子进程中执行 ret = p.apply(apply_async,args=(i,)) #没有返回值,要想所有任务能够顺利的执行完毕 p.close() #关闭进程池,用户不能再向这个池中提交任务了 p.join() #必须先close再join,阻塞直到进程池中所有任务执行完毕 #有返回值的情况下 ret.get()#get不能在提交任何之后立刻执行,应该先提交所有任务,再通过get得到返回值 #map()方法 #异步提交的简化版本 #自带close和join方法
#例:进程池与多进程的效率对比 import time from multiprocessing import Pool,Process def func(num): print('做了第%s件衣服'%num) if __name__ == '__main__': #以进程池的形式执行500个任务 strat = time.time() p = Pool(4) for i in range(500): p.apply_async(func,args=(i,)) p.close() p.join() print(time.time() - strat) #以多进程的形式执行100个任务 strat = time.time() p_lst = [] for i in range(100): p = Process(target=func,args=(i,)) p.start() p_lst.append(p) for p in p_lst: p.join() print(time.time() - strat)
#以完全同步的方式提交任务(apply) import os import time from multiprocessing import Pool def task(num): time.sleep(1) print('%s:%s'%(num,os.getpid())) return num**2 #进程池有IPC机制,可以直接return if __name__ == '__main__': p = Pool(4) for i in range(20): ret = p.apply(task,args=(i,)) #提交任务的方法,同步提交(自带join阻塞) print(ret) #任务逐个提交并完成后再返回
#以完全异步的方式提交任务(apply_async) import os import time from multiprocessing import Pool def task(num): time.sleep(1) print('%s:%s'%(num,os.getpid())) return num**2 if __name__ == '__main__': p = Pool(4) #默认值为os.cpu_count() or 1 ,一般为os.cpu_count()+1 res_lst = [] for i in range(20): ret = p.apply_async(task,args=(i,)) res_lst.append(ret) for res in res_lst: #异步方式取返回值 print(res.get())