Queue(先进先出队列)
队列特点
队列特点:先进先出(FIFO)——先进队列的元素先出队列。来源于我们生活中的队列(先排队的先办完事)。
import queue
q = queue.Queue() #调用队列生成对象
q.put(1) #存放第一个值到队列
q.put(2) #存放第二个值到队列
print('get frist one:%s' % q.get()) # 获取队列的第一个值
print('get second on:%s ' % q.get()) # 获取队列的第二个值
- 先进先出原则第一次存放的是1,第二次存放的是2,那么我们在获取值得时候,第一次获取的就是1,第二次就是2
看下面的例子如果队列里没有值怎么办?他会等待直到有数据为止:
import queue
q = queue.Queue() # 调用队列生成对象
q.put(1) #存放第一个值到队列
q.put(2) #存放第二个值到队列
a = q.get() # 获取队列的第一个值
print('get frist one:%s' % a)
b = q.get() # 获取队列的第二个值
print('get second one:%s' % b)
c = q.get() # 获取队列的第三个值
print('get third one:%s' % c)
#结果:
'''
get frist one:1
get second one:2
#这里没有获取到值堵塞住,一直在等待着值进来~
'''
- 如果不想让他等待,不管是否队列里都取数据,可以使用
get_nowait()
,但是如果队列中没有数据就会报错!
import queue
q = queue.Queue() # 调用队列生成对象
q.put(1) #存放第一个值到队列
q.put(2) #存放第二个值到队列
a = q.get() # 获取队列的第一个值
print('get frist one:%s' % a)
b = q.get() # 获取队列的第二个值
print('get second one:%s' % b)
c = q.get_nowait() # 获取队列的第三个值,使用:get_nowait() 不堵塞!
print('get third one:%s' % c)
- 如果队列为空的时候可以通过异常处理进行捕获:
import queue
q = queue.Queue() # 调用队列生成对象
q.put(1) #存放第一个值到队列
q.put(2) #存放第二个值到队列
try:
a = q.get() # 获取队列的第一个值
print('get frist one:%s' % a)
b = q.get() # 获取队列的第二个值
print('get second one:%s' % b)
c = q.get_nowait() # 获取队列的第三个值,使用:get_nowait() 不堵塞!
print('get third one:%s' % c)
except queue.Empty as q_error:
print('The Queue is empty!')
- 同样的如果队列长度为2,如果队列满了之后,同样他也是等待,直到有位置才会继续如下代码:
import queue
q = queue.Queue(2) # 调用队列生成对象,2:设置队列长度为2
q.put(1) # 存放第一个值到队列
print('put value 1 done')
q.put(2) # 存放第二个值到队列
print('put vlaue 2 done')
q.put(3) # 存放第三个值到队列
print('put value 3 done')
#结果:
'''
put value 1 done
put vlaue 2 done
#这里会一直等待~
'''
- 同样如果存放数值的时候如果不想让他等待,使用
put_nowait()
但是队列无法存放后会报错!
import queue
q = queue.Queue(2) # 调用队列生成对象,2:设置队列长度为2
q.put(1) # 存放第一个值到队列
print('put value 1 done')
q.put(2) # 存放第二个值到队列
print('put vlaue 2 done')
# q.put(33, block=False) # 不堵塞
# q.put(33, block=False, timeout=2) # 不堵塞,等待2秒
q.put_nowait(3) # 存放第三个值到队列,使用:put_nowait() 不堵塞!
print('put value 3 done')
- 也可以使用empty()方法判断队列是否为空
import queue
# 队列最大长度,
# qsize()真实队列个数
# maxsize 最大支持的个数
q = queue.Queue(2) # 队列最大长度
print(q.empty()) # 检查是否为空(初始默认是空)
q.put(11)
q.put(22)
print(q.empty())
print(q.qsize()) # 获取队列长度
生产者消费者模型
什么是生产者消费者模型?
在工作中,大家可能会碰到这样一种情况:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程、进程等)。产 生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。在生产者与消费者之间在加个缓冲区,我们形象的称之为仓库,生产者负责往仓库了进商 品,而消费者负责从仓库里拿商品,这就构成了生产者消费者模型。结构图如下:
生产者消费者模型的优点:
1、解耦
假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(也就是耦合)。将来如果消费者的代码发生变化, 可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降低了。
举个例子,我们去邮局投递信件,如果不使用邮筒(也就是缓冲区),你必须得把信直接交给邮递员。有同学会说,直接给邮递员不是挺简单的嘛?其实不简单,你必须 得认识谁是邮递员,才能把信给他(光凭身上穿的制服,万一有人假冒,就惨了)。这就产生和你和邮递员之间的依赖(相当于生产者和消费者的强耦合)。万一哪天邮递员换人了,你还要重新认识一下(相当于消费者变化导致修改生产者代码)。而邮筒相对来说比较固定,你依赖它的成本就比较低(相当于和缓冲区之间的弱耦合)。
2、支持并发
由于生产者与消费者是两个独立的并发体,他们之间是用缓冲区作为桥梁连接,生产者只需要往缓冲区里丢数据,就可以继续生产下一个数据,而消费者只需要从缓冲区了拿数据即可,这样就不会因为彼此的处理速度而发生阻塞。
接上面的例子,如果我们不使用邮筒,我们就得在邮局等邮递员,直到他回来,我们把信件交给他,这期间我们啥事儿都不能干(也就是生产者阻塞),或者邮递员得挨家挨户问,谁要寄信(相当于消费者轮询)。
3、支持忙闲不均
缓冲区还有另一个好处。如果制造数据的速度时快时慢,缓冲区的好处就体现出来了。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。 等生产者的制造速度慢下来,消费者再慢慢处理掉。
为了充分复用,我们再拿寄信的例子来说事。假设邮递员一次只能带走1000封信。万一某次碰上情人节(也可能是圣诞节)送贺卡,需要寄出去的信超过1000封,这时 候邮筒这个缓冲区就派上用场了。邮递员把来不及带走的信暂存在邮筒中,等下次过来 时再拿走。
简单的生产者消费者模型:
import queue
import threading
import time
q = queue.Queue()
# 生成者(client)
def productor(arg):
# 序号加包子,将做好的包子放到篮子(队列)里
q.put(str(arg) + '包子')
# 创建30个包子
for i in range(30):
t = threading.Thread(target=productor, args=(i,))
t.start()
# ============================================================== #
# 消费者(server)
def consumer(arg):
while True:
# arg(0-3)吃包子得人, q.get()从篮子(队列)里取包子,包子有序号
print(arg, q.get())
time.sleep(2)
# 三个线程一起吃包子
for j in range(3):
t = threading.Thread(target=consumer, args=(j,))
t.start()
PriorityQueue(优先级队列)
队列特点
队列特点: 根据优先级处理,数字最小的优先级最高
import queue
q = queue.PriorityQueue() # 根据优先级处理,数字最小的优先级最高
# put() 参数一为优先级,第二个参数是value
q.put((3, "alex3"))
q.put((2, "alex2"))
q.put((1, "alex1"))
print(q.get())
deque(双向队列)
队列特点
队列特点: 两边都能插入数据,都能取数据
import queue
q= queue.deque() #双向队列
q.append((123))
q.append(234)
q.appendleft(456)
print(list(q))
q.pop()
q.popleft()
print(list(q))
append(x)
将x添加到 deque 的右侧。
appendleft(x)
将x添加到 deque 的左侧。
clear()
从留下长度为 0 的 deque 中移除所有元素。
count(x)
统计 deque 中值为x的个数。
extend(iterable)
通过追加元素从可迭代参数扩展的 deque 的右侧。
extendleft(iterable)
通过将附加元素从扩展 deque 的左侧可迭代。请注意,左边的一系列附加结果在扭转可迭代参数中元素的顺序。
pop()
删除并从右侧的双端队列中返回的元素。如果没有元素存在,提出了IndexError。
popleft()
移除并返回一个元素从 deque 的左侧。如果没有元素存在,提出了IndexError。
remove(value)
删除值的第一个匹配项。如果未找到,引发ValueError。
reverse()
翻转 deque 的元素,然后返回None。
rotate(n)
deque中的元素向右移动n个位置。如果n是负数的向左移动。向右移动一步相当于: d.appendleft(d.pop())。
maxlen
maxlen
Deque 的最大长度。如果没有边界,则返回None。