Python之queue模块以及生产消费者模型-队列
一、队列
队列类似于一条管道,元素先进先出,进put(arg)
,取get()
有一点需要注意的是:队列都是在内存中操作,进程退出,队列清空,另外,队列也是一个阻塞的形态.
比喻:队列就好比排队打饭一样,先排的先打到饭, ,栈就好比往桶里倒水,后倒的先满出来
1.队列分类
队列有很多中,但都依赖模块queue
2.队列的方法
3.队列基本操作
3.1单向队列
import queue q=queue.Queue(5) #如果不设置长度,默认为无限长 print(q.maxsize) #注意没有括号 q.put(123) #添加值到队列 q.put(456) q.put(789) q.put(100) q.put(111) #q.put(111666666) #添加值超过队列长就会堵塞 print(q.queue) #打印整个队列 print(q.get()) print(q.get()) #元素是先进先出 print(q.get()) #获取数据(每次获取一条, 获取后该元素从队列清除了) print(q.queue) #打印整个队列
如此打印时候是阻塞的,为什么呢,因为创建了5个元素长度的队列,但我put进去了6个,所以就阻塞了.如果少写一个能显示出正确的123
.
3.2优先级队列
import queue
#优先级队列: 根据数字排序,优先打印数字小的元素 q = queue.PriorityQueue() q.put((3,'aaaaa')) q.put((4,'bbbbb')) q.put((1,'ccccc')) q.put((6,'ddddd333')) q.put((3,'ddddd')) q.put((5,'ddddd')) print(q.get()) print(q.get()) print(q.get()) print(q.get()) print(q.get()) print(q.get())
3.3双线队列
import queue q = queue.deque() q.append(123) q.append(456)#添加元素到队列 q.append(4999) q.appendleft(780)#添加元素到队列 q.appendleft(1000) print(q.pop())#获取队列元素,先进后出(获取后元素从队列清除) print(q.pop()) print(q.popleft())#获取队列元素(先进后出) print(q.popleft())
3.4队列和栈操作
#!/usr/bin/env python # -*- coding: utf-8 -*- from queue import LifoQueue from queue import Queue def enter_stack(): '''栈的基本操作''' s = LifoQueue(5)#栈的长度5元素 print("---------------- 添加元素前 --------------------") print(s.empty()) # 判断栈是否为空 print(s.queue) # 输出整个栈 print(s.full()) # 判断栈是否已经满了 print(s.qsize()) # 输出栈长度 print("---------------- 添加元素(进栈) --------------------") s.put(1) s.put(2) s.put(3) s.put(4) s.put(5) print(s.empty()) # 判断栈是否为空 print('打印整个栈:',s.queue) # 输出整个栈 print(s.full()) # 判断栈是否已经满了 print(s.qsize()) # 输出栈长度 print('-----------------取值(出栈)---------------------') print(s.get()) print(s.get()) print(s.get()) def _queue(): '''队列基本操作''' q = Queue(5) # 创建一个长度为5的队列 print("---------------- 添加元素前 --------------------") print(q.empty()) # 判断队列是否为空 print(q.queue) # 输出整个队列 print(q.full()) # 判断队列是否已经满了 print(q.qsize()) # 输出队列长度 print("---------------- 添加元素(入队列) --------------------") q.put("元素1") q.put("元素2") q.put("元素3") q.put("元素4") q.put_nowait("元素5") # 非阻塞入队 print(q.empty()) # 判断队列是否为空 print('打印整个队列:',q.queue) # 输出整个队列 print(q.full()) # 判断队列是否已经满了 print(q.qsize()) # 输出队列长度 print('---------------获取元素(出队列)-----------------------') print(q.get()) print(q.get()) print(q.get()) if __name__ == '__main__': #栈特点:后进先出, 队列特点:先进先出 enter_stack() _queue()
二、栈
栈的特点: 先进后出
from queue import LifoQueue s = LifoQueue(5) print('------------添加元素(简称:进栈)------------------') print(s.empty()) # 判断栈是否为空 print(s.queue) # 输出整个栈 print(s.full()) # 判断栈是否已经满了 print(s.qsize()) # 输出栈长度 #添加元素到栈里 s.put(1) s.put(2) s.put(3)#如果添加元素超过长度、就会堵塞线程 print('------------添加元素之后-----------------------') print(s.empty()) # 判断栈是否为空 print(s.queue) # 输出整个栈 print(s.full()) # 判断栈是否已经满了 print(s.qsize()) # 输出栈长度 print('------------获取元素(简称:出栈)----------------') print(s.get()) print(s.get())#后进先出 print(s.get())#后进先出
三、生产消费者模型
1.解决什么问题,使用场景
从下面图中可以发现生产者和消费者之间用中间类似一个队列一样的东西串起来。这个队列可以想像成一个存放产品的“仓库”,生产者只需要关心这个“仓库”,并不需要关心具体的消费者,对于生产者而言甚至都不知道有这些消费者存在。对于消费者而言他也不需要关心具体的生产者,到底有多少生产者也不是他关心的事情,他只要关心这个“仓库”中还有没有东西。这种模型是一种松耦合模型。这样可以回答我上面提出的第一个问题。这个模型的产生就是为了复用和解耦。比如常见的消息框架(非常经典的一种生产者消费者模型的使用场景)ActiveMQ。发送端和接收端用Topic进行关联。这个Topic可以理解为我们这里“仓库”的地址,这样就可以实现点对点和广播两种方式进行消息的分发。
#!/usr/bin/env python # -*- coding: utf-8 -*- #________________________________________生成器实现 生产者与消费者模型___________________________________________ import time cacheList = []#队列 cacheListLen =5#最大元素 def is_full(): """ 判断缓冲区队列是否已经满了 :return: """ return len(cacheList)==cacheListLen def Producer(name): ##生产者 '''生产者''' b=1 while True: if not is_full():#判断队列是否已满 print("生产者【%s】正在生成游戏机%s..."%(name,b)) time.sleep(0.5) print("【%s】已经生产游戏机完成%s"%(name,b)) cacheList.append('游戏机'+str(b)) b+=1 else: print('生产足够多了...') yield #遇到yield,返回到调用的位置 def Consumer(name): ##消费者 '''消费者''' print('[%s]正准备购买游戏机'%(name)) while True: item =yield #item表示send()传过来的值 print('[%s]购买游戏机成功'%(name)) print('item为:',item) if __name__ == '__main__': producer =Producer("小高")#实例化生成器(生产者) next(producer)#next方法是调用生成器,遇到yield 停止 #实例化生成器或者叫创建生器对象,遇到 next才会调用生成器 consumers = [Consumer("消费者%s"%(i+1)) for i in range(10)] #这里是创建是个生成器对象(消费者) for Consumer in consumers: if not cacheList: print("目前商店没有游戏库存...") else: if not is_full(): #如果队列没满 next(producer)#调用消费者 else: item = cacheList.pop(0)#满了,移除cacheList的第0个元素 next(Consumer)#调用消费者 Consumer.send(item)#表示传值给生成器的yield #__________________________________________________队列实现生产者消费者模式___________________________________________________________________________ #!/usr/bin/env python # -*- coding: utf-8 -*- import queue,threading,time q=queue.Queue()#创建队列不限制长度 def product(arg): '''生产者''' while True: time.sleep(0.5) q.put(str(arg)+'包子') #往队列添加元素 print('生产了:',str(arg)+'包子') def consumer(arg): '''消费者''' while True: print('已消费:',arg,q.get()) #获取队列元素 time.sleep(2) for i in range(3): t=threading.Thread(target=product,args=(i,))#使用多线程 t.start() for j in range(10): t=threading.Thread(target=consumer,args=(j,)) t.start()
import time import queue import threading q = queue.Queue(10) # 生成一个队列,用来保存“包子”,最大数量为10 # 生产者 def productor(i): # 厨师不停地每2s做一个包子 while True: q.put("厨师%s做的包子!" % i) print("厨师{}做了一个包子".format(i)) time.sleep(2) def consumer(i): # 顾客不停地每1s吃一个包子 while True: print("顾客%s吃了一个%s" % (j, q.get())) time.sleep(1) # 实例化3个生产者(厨师) for i in range(3): t = threading.Thread(target=productor, args=(i,)) t.start() # 实例化10个消费者 for j in range(10): v = threading.Thread(target=consumer, args=(j,)) v.start()
总结
解决程序解耦,较少的资源解决高并发的问题
相关连接:
https://www.cnblogs.com/xingxingnbsp/p/12941717.html.............python 栈&队列&列表的区别
https://blog.csdn.net/weixin_39895481/article/details/110627013 .......................队列应用场景
https://www.jianshu.com/p/af384cb9124e....................................Python中的生产者与消费者模式
https://baijiahao.baidu.com/s?id=1715945634773884801&wfr=spider&for=pc .......消息队列MQ