(multiprocess.Lock)锁和(multiprocess.Queue)队列

进程同步(锁)

现在有这么情况,我们模仿了一个抢票软件,文件里有十张票,一共有二十个人去抢,抢走一张就少一张,二十个人我们为了达到并发的效果,显然是要以多进程来实现的,那么这时候就有一个问题,好几个人同时看到的都是还剩下10张票,每个人都抢了一张,结果只是剩下19。问题就是这个,应该是某个人在抢的时候,其他人无法抢才对。所以这时候我们就需要给这个步骤上锁,只要有进程在执行的时候,别的进程就无法执行这段代码,只能等待。

这时候我们需要在multiprocessing中导入一个Lock类。

#文件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 是把所有的子进程变成了串行


 

在我们主进程的代码里面生成一个锁(只能在这里,才是一个锁,在上面的方法里的话就是生成多个锁了),然后把这个锁的对象当做参数传进去。lock.acquire()就是开启锁,只有一个进程能进去执行,下一个进程想进去必须要等lock.release()释放才能进去。这就很好的实现了这段代码只有一个进程才能跑,操作文件同时只能一个进程。

加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全

那我速度降低了我肯定不乐意啊,有没有什么方法能够两全其美呢?还真有。

那就是队列和管道

队列≈管道+锁

 

队列

举个栗子。

凉轻松去一家包子店吃包子,厨师只管自己做包子,做完一个就放一个进大盘子里,凉轻松只管从大盘子里拿包子,没了就等着。这个大盘子就是所谓的队列。

创建方式

Queue([maxsize])

maxsize是队列中允许最大项数,省略则无大小限制。

1 q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
2 q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常. 3 q.get_nowait():同q.get(False),就是说不等,有就拿,没有就砸店 4 q.put_nowait():同q.put(False),同上(厨师:我为什么要砸店?) 5 q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。 6 q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。 7 q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样

栗子

from multiprocessing import Process,Queue
import time,random

def producer(q,name,food): '''生产者''' for i in range(3): print(f'{name}生产了{food}{i}') time.sleep(random.randint(1, 3)) res = f'{food}{i}' q.put(res) # q.put(None) def consumer(q,name): '''消费者''' while True: res = q.get(timeout=5) if res is None:break time.sleep(random.randint(1,3)) print(f'{name}吃了{res}') if __name__ == '__main__': q = Queue() p1 = Process(target=producer,args=(q,'rocky','包子')) p2 = Process(target=producer,args=(q,'mac','韭菜')) p3 = Process(target=producer,args=(q,'nick','蒜泥')) c1 = Process(target=consumer,args=(q,'成哥')) c2 = Process(target=consumer,args=(q,'浩南哥')) p1.start() p2.start() p3.start() c1.start() c2.start() p1.join()# 写了三个生产者的join,保证了生产者生产完毕,也就是生产者进程全结束了。 p2.join()#为什么这个要写在c1和c2 的start下面,因为这样才有边造边吃的效果 p3.join() q.put(None)# 几个消费者put几次,一旦消费者接收到None,就会挂掉。 q.put(None)

既然讲到了包子,顺便也讲一下生产者和消费者!

生产者和消费者

很直白,厨师就是生产者,凉轻松就是消费者。

生产者: 生产数据的任务

消费者: 处理数据的任务

既然这样,我们就可以把上面的所有东西串起来了,用一个栗子来看

不行不行,还要讲一下一个东西。

JoinableQueue

这玩意儿也是在multiprocessing里的。

from multiprocessing import Process,Queue,JoinableQueue


q = JoinableQueue()

q.put('zhao') # 放队列里一个任务 q.put('qian') print(q.get()) q.task_done() # 完成了一次任务 print(q.get()) q.task_done() # 完成了一次任务 q.join() #计数器不为0的时候 阻塞等待计数器为0后通过 # 想象成一个计数器 :put +1 task_done -1

每当往JoinableQueue队列里放一个东西,他的任务数就+1,每当拿走一个,我们就手动调用一下q的task_done方法,任务数就会-1,队列也是由join方法的,当任务数为0的时候,join才会不阻塞。

现在可以上总的栗子了!

综合实例

from multiprocessing import Process,Queue,JoinableQueue
import time,random

def producer(q,name,food): '''生产者''' for i in range(3): print(f'{name}生产了{food}{i}') time.sleep(random.randint(1, 3)) res = f'{food}{i}' q.put(res) # q.put(None) def consumer(q,name): '''消费者''' while True: res = q.get() # if res is None:break time.sleep(random.randint(1,3)) print(f'{name}吃了{res}') q.task_done() # if __name__ == '__main__': q = JoinableQueue() p1 = Process(target=producer,args=(q,'rocky','包子')) p2 = Process(target=producer,args=(q,'mac','韭菜')) p3 = Process(target=producer,args=(q,'nick','蒜泥')) c1 = Process(target=consumer,args=(q,'成哥')) c2 = Process(target=consumer,args=(q,'浩南哥')) p1.start() p2.start() p3.start() c1.daemon = True c2.daemon = True c1.start() c2.start() p1.join() p2.join() p3.join() # 生产者生产完毕 # q.put(None)# 几个消费者put几次 # q.put(None) q.join() # 分析 # 生产者生产完毕--这是主进程最后一行代码结束--q.join()消费者已经取干净了,没有存在的意义了. #这是主进程最后一行代码结束,消费者已经取干净了,没有存在的意义了.守护进程的概念.

rocky生产了包子0
mac生产了韭菜0
nick生产了蒜泥0
nick生产了蒜泥1
mac生产了韭菜1
rocky生产了包子1
nick生产了蒜泥2
浩南哥吃了韭菜0
mac生产了韭菜2
成哥吃了蒜泥0
rocky生产了包子2
成哥吃了蒜泥1
浩南哥吃了包子0
浩南哥吃了蒜泥2
浩南哥吃了包子1
成哥吃了韭菜1
浩南哥吃了韭菜2
成哥吃了包子2

至于为什么这么乱,因为操作系统的调度谁也不知道他是怎样的,你不知道他先执行哪个进程等等等等等。最后程序结束。

转载于:https://www.cnblogs.com/whnbky/p/11528075.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值