并发编程:互斥锁,生产者,消费者模型
一 守护进程:
守护进程也就是一个进程会随着另一个进程(被守护进程)的结束而结束:
from multiprocessing import Process import time def func(name): time.sleep(2) print('%s is running'%name) if __name__ == '__main__': # t=Process(target=func,args=('feng',)) t=Process(target=func,kwargs={'name':'feng'}) t.daemon=True#我们通过把子进程变成守护进程。 t.start() print('zhu'.center(50,'*')) 输出结果:***********************zhu************************
从结果我们可以看出,主进程一结束,子进程就结束。
二 互斥锁:保证每一个我们的子进程完整的运行完之后才会运行下一个子进程,但是在当前子进程阻塞的时候可以去进行新的子进程的创建。
from multiprocessing import Process,Lock import time import random mutex=Lock() def task1(lock): lock.acquire()#不能连续的acquire,必须抢一次,释放一次才能继续抢。 print('task1:name1') time.sleep(random.randint(1,2)) print('task1:age1') time.sleep(random.randint(1, 2)) print('task1:sex1') time.sleep(random.randint(1, 2)) lock.release() def task2(lock): lock.acquire() print('task1:name2') time.sleep(random.randint(1, 2)) print('task1:age2') time.sleep(random.randint(1, 2)) print('task1:sex2') time.sleep(random.randint(1, 2)) lock.release() def task3(lock): lock.acquire() print('task1:name3') time.sleep(random.randint(1, 2)) print('task1:age3') time.sleep(random.randint(1, 2)) print('task1:sex3') time.sleep(random.randint(1, 2)) lock.release() if __name__ == '__main__': p1=Process(target=task1,args=(mutex,)) p2=Process(target=task2,args=(mutex,)) p3=Process(target=task3,args=(mutex,)) p1.start() p2.start() p3.start()
我们从multiprocessing模块导入Lock,通过Process实例化出三个对象p1,p2,p3,这三个对象在执行的时候就是三个进程,互斥锁会保证三个进程之间公平竞争cpu资源
join与互斥锁的区别:二者原理一样,把并发变成串行,保证有序,join人为的指定顺序
应该谁先启动谁先来用,如果使用join那么我们的顺序就固定了,必须p1先开始
而互斥锁是所有的进程平等的争抢,谁先抢到谁先用。抢到之后运行完了一定要要释放,要不然别的进程没办法拿到锁。
我们使用join同样可以实现一个串行的效果:
from multiprocessing import Process import time import random def task1(): print('task1:name1') time.sleep(random.randint(1,2)) print('task1:age1') time.sleep(random.randint(1, 2)) print('task1:sex1') time.sleep(random.randint(1, 2)) def task2(): print('task1:name2') time.sleep(random.randint(1, 2)) print('task1:age2') time.sleep(random.randint(1, 2)) print('task1:sex2') time.sleep(random.randint(1, 2)) def task3(): print('task1:name3') time.sleep(random.randint(1, 2)) print('task1:age3') time.sleep(random.randint(1, 2)) print('task1:sex3') time.sleep(random.randint(1, 2)) if __name__ == '__main__': p1=Process(target=task1,) p2=Process(target=task2,) p3=Process(target=task3,) p1.start() p1.join() p2.start() p2.join() p3.start() p3.join()
三 基于互斥锁模拟抢票系统:
互斥锁可以有选择的让一部分代码(修改共享数据的代码)串行。而join则让所有的代码都按照一个确定的顺序运行。
#文件db的内容为:{"count":1} #注意一定要用双引号,不然json无法识别 from multiprocessing import Process,Lock import time,json,random def search(): dic=json.load(open('db.txt')) print('\033[43m剩余票数%s\033[0m' %dic['count']) def get(): dic=json.load(open('db.txt')) time.sleep(0.1) #模拟读数据的网络延迟 if dic['count'] >0: dic['count']-=1 time.sleep(0.2) #模拟写数据的网络延迟 json.dump(dic,open('db.txt','w')) print('\033[43m购票成功\033[0m') def task(lock): search() lock.acquire() get() lock.release() if __name__ == '__main__': lock=Lock() for i in range(100): #模拟并发100个客户端抢票 p=Process(target=task,args=(lock,)) p.start()
同样的我们也可以使用join实现一个抢票系统,但是这个时候,每次只能一个人来操作,也就是说,每次只能提供一个人进行查票或则买票,其他人只有等到前一个用户退出之后
才能进行查票或则购票,而互斥锁可以提供多个用户同时查票,单个用户购票。
#文件db的内容为:{"count":1} #注意一定要用双引号,不然json无法识别 from multiprocessing import Process,Lock import time,json,random def search(): dic=json.load(open('db.txt')) print('\033[43m剩余票数%s\033[0m' %dic['count']) def get(): dic=json.load(open('db.txt')) time.sleep(0.1) #模拟读数据的网络延迟 if dic['count'] >0: dic['count']-=1 time.sleep(0.2) #模拟写数据的网络延迟 json.dump(dic,open('db.txt','w')) print('\033[43m购票成功\033[0m') def task(lock): search() get() if __name__ == '__main__': lock=Lock() for i in range(100): #模拟并发100个客户端抢票 p=Process(target=task,args=(lock,)) p.start()
总结:其实基于互斥锁和join这两种方式效率都是比较低的,但是互斥锁要比join效率更高,而且互斥锁可以实现CPU利用更大化,同时开启多个进程,我们可以让多个进程都查看余票数,但是这个余票数是不可靠的,因为多个进程都执行到这一步,但是最终只有一个进程能拿到这个票,也就是说大家所看到的其实是一张票。而如果我们使用join模式的话,那么只要我们能查看到票就能买到票。
四 ICP机制:我们又称他为基于消息的机制。
进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的
进程之间的通信:进程之间是相互独立的,所以进程之间的通讯需要介质:管道和队列,我们优先使用队列。
队列的通讯是一个有序的过程,先进先出,不会导致我们的信息混乱,而管道的两端都可以进行存取,所以容易造成错乱。
进程之间的通信:
必须找到一种介质:
介质需要满足的条件是:
1 是所有进程共享的
2 必须是内存空间
3 这种机制需要帮我们自动处理锁的问题
列队:队列的一些用法,其实底层就是互斥锁+管道的原理。
队列:先进先出,里面不应该存放数据量较大的数据,又来存方进程之间沟通的消息,数据量不应该过大。 1 共享的空间 2 是内存空间 3 自动处理锁的问题
from multiprocessing import Queue
q=Queue(3),最多只能put三次,这里的3受限于内存空间的大小,也就是说,maxsize是可以无限大的,但是那样没意义。 q.put()#括号里面obj,在Python中一切皆为对象。 q.put('first',block=False) q.put() q.get() q.get() q.get('first',block=True,timeout=3)#当block为True时,管道中没有东西给你来取的时候,就会一直等待,程序不会终止。 q.get('first',block=False,timeout=3)#这个时候如果管道中没有东西取,等待三秒之后就会抛出异常,我们可以使用try来处理此异常。 q.put()#括号里面obj q.put('first',block=True,timeout=3)#等3秒 q.put('first',block=True,timeout=3)其中block说的是阻塞与非阻塞的问题,而timeout只有在非阻塞的情况下运用,规定等待一段时间之后抛出异常, q.put()
不得不感慨multiprocessing真的是一个强大的模块。。。。。。。。
五 生产者,消费者模型:
生产者消费者模型主要分为:生产者与消费者两大类 生产者:把负责造数据的任务称为生产则, 消费者:接收生产者造出的数据来作进一步处理。 生产者消费者模型三要素:1 生产者, 2 消费者 , 3 队列 使用场景:一定是我们的程序中出现明显的两类任务,一类人物是负责生产而另一类任务是负责处理生产的数据的 好处:1 实现了生产者与消费者解耦合 2 平衡了生产力与消费力,生产者可以一直不停的生产,消费者可以一直不停的处理,因为二者不再直接沟通。而是直接从阻塞队列里取,
阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。