一、Queue
Queue是python标准库中的线程安全的队列(FIFO)实现,提供了一个适用于多线程编程的先进先出的数据结构,即队列,用来在生产者和消费者线程之间的信息传递
队列在多线程中可以共享,替代全局变量,无需加互斥锁。
队列可以并发的派多个线程,对排列的线程处理,并且每个需要处理线程只需要将请求的数据放入队列容器的内存中,线程不需要等待,当排列完毕处理完数据后,线程再准时来取数据即可。请求数据的线程只与这个队列容器存在关系,处理数据的线程down掉不会影响到请求数据的线程,队列会派给其他线程处理这分数据,它实现了解耦,提高效率。
队列内会有一个有顺序的容器,列表与这个容器是有区别的,列表中数据虽然是排列的,但数据被取走后还会保留,而队列中这个容器的数据被取后将不会保留。当必须在多个线程之间安全地交换信息时,队列在线程编程中特别有用。
二、Python四种类型的队例:
- Queue:FIFO 即first in first out 先进先出
- LifoQueue:LIFO 即last in first out 后进先出
- PriorityQueue:优先队列,级别越低,越优先
- deque:双边队列
from queue import Queue, LifoQueue, PriorityQueue
1、Queue 先进先出队列:
q = Queue(maxsize=5)
初始化一个队列,默认是不限定队列的长度,也可以通过参数指定队列中数据的maxsize,如果maxsize小于或者等于0,队列大小没有限制。
put(i,timeout=2) 方法向队列中添加元素,timeout以设置队列中数据满了之后的等待时间(默认一直等待)
q.put(999, block=False) # block=False,往队列中添加数据,不等待,如果队列中数据已满,则会报错
from queue import Queue
q = Queue(maxsize=5) #限定最长为5
for i in range(5):
q.put(i,timeout=2)
print(q)
while not q.empty():
print(q.get())
#输出
0
1
2
3
4
2、LifoQueue 后进先出队列:
创建队列和向队列中添加元素与先进先出队列完全一致
from queue import LifoQueue
q = LifoQueue(5)
for i in range(5):
q.put(i,timeout=2)
print(q)
while not q.empty():
print(q.get())
#输出
4
3
2
1
0
3、优先队列:
from queue import PriorityQueue
import random
q = PriorityQueue(5)
for i in range(5):
x = random.randint(1,99)
q.put((x,f"{i}a"),timeout=2)
print(q)
while not q.empty():
print(q.get())
#输出
(37, '1a')
(44, '2a')
(52, '4a')
(76, '3a')
(77, '0a')
put传递的第一个参数为元组,元组的第一个元素表示数据的优先级
队列中的数据为元组类型:元组的第一个元素表示数据的优先级,优先级越小的先出来
关于优先级,尽量使用数值,如果全是字符串,会按ASCII码进行排序
通用方法
1、往队列中添加数据:q.put( item, block=True, timeout=None)
- item为添加的对象
- True 往队列中添加数据,如果队列中数据已满,一直等待
- bloke=False,往队列中添加数据,不等待,如果队列中数据已满,则报错
- timeout不设置,一直等待
- timeout设置后等待超过时间则报错
其非阻塞版本为`put_nowait`等同于put(item, block=False)
2、从队列中移除并返回一个数据:q.get([block[, timeout]])
block和timeout用法与put一致
其非阻塞方法为`get_nowait()`相当与get(block=False)
3、判断队列中数据是否为空:empty方法 q.empty()
- 队列中数据为空,返回True
- 队列中数据不为空,返回False
4、判断队列中数据是否已满:full q.full()
- 队列中数据已满,返回True
- 队列中数据不满,返回False
5、获取队列中任务数量(多少条数据) q.qsize()
6、task_done()
意味着之前入队的一个任务已经完成。由队列的消费者线程调用。每一个get()调用得到一个任务,接下来的task_done()调用告诉队列该任务已经处理完毕。
如果当前一个join()正在阻塞,它将在队列中的所有任务都处理完时恢复执行(即每一个由put()调用入队的任务都有一个对应的task_done()调用)。
7、join()
阻塞调用线程,直到队列中的所有任务被处理掉。
只要有数据被加入队列,未完成的任务数就会增加。当消费者线程调用task_done()(意味着有消费者取得任务并完成任务),未完成的任务数就会减少。当未完成的任务数降到0,join()解除阻塞。