1. Queue 概念
队列 Queue 多应用在多线程应用中,多线程访问共享变量。对于多线程而言,访问共享变量时,队列 Queue 是线程安全的。
Python Queue 模块有三种队列及构造函数:
- Python Queue模块的FIFO队列先进先出。
class Queue.Queue(maxsize)
- LIFO类似于堆,即先进后出。
class Queue.LifoQueue(maxsize)
- 还有一种是优先级队列级别越低越先出来。即队列中的元素是一个元祖类型,(优先级级别,数据)。
class Queue.PriorityQueue(maxsize)
2. Queue 常用的方法
q = Queue.Queue()
函数 | 说明 |
---|---|
q.qsize() | 返回队列的大小 |
q.empty() | 如果队列为空,返回True,反之False |
q.full() | 如果队列满了,返回True,反之False |
q.get([block[, timeout]]) | 获取队列,timeout等待时间 |
q.get_nowait() | 相当q.get(False),无阻塞的向队列中get任务,当队列为空时,不等待,而是直接抛出empty异常,重点是理解block = False: |
q.put(item) | 非阻塞写入队列,timeout等待时间 |
q.put_nowait(item) | 相当q.put(item, False),无阻塞的向队列中添加任务,当队列为满时,不等待,而是直接抛出full异常,重点是理解block = False |
q.task_done() | 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号 |
q.join() | 阻塞等待队列中任务全部处理完毕,需要配合queue.task_done使用 |
重点方法说明:
2.1 get() 方法
from Queue import Queue
Queue.get(self, block=True, timeout=None)
调用队列对象的 get() 方法从队头删除并返回一个项目。可选参数为 block,默认为 True。
- 如果队列为空且 block 为 True,get() 就使调用线程暂停,直至有项目可用。
- 如果队列为空且 block 为 False,队列将引发 Empty 异常。
2.2 put() 方法
from Queue import Queue
Queue.put(self, item, block=True, timeout=None)
调用队列对象的 put() 方法在队尾插入一个项目。put() 有三个参数,第一个 item 为必需的,为插入项目的值;第二个 block 为可选参数,默认为 True。
- 如果队列当前为空且 block 为 True,put() 方法就使调用线程暂停,直到空出一个数据单元。
- 如果block为 False,put 方法将引发 Full 异常
从上面说明可以看到,当一个队列为空的时候如果再用 get 取则会堵塞,所以取队列的时候一般是用到 get_nowait() 方法,这种方法在向一个空队列取值的时候会抛一个 Empty 异常,所以更常用的方法是先判断一个队列是否为空,如果不为空则取值。
from Queue import Queue
my_queue = Queue(maxsize=10)
# 定义队列时有一个默认的参数maxsize, 如果不指定队列的长度,即maxsize=0,那么队列的长度为无限长,如果定义了大于0的值,那么队列的长度就是maxsize。
a = [1, 2, 3]
my_queue.put(a)
print my_queue.get()
print my_queue.get() # 再次调用时进程一直未结束,处于等待队列中有值的状态
3. 注意事项
3.1 阻塞模式
import Queue
q = Queue.Queue(10)
......
for i in range(10):
q.put('A')
time.sleep(0.5)
这是一段极其简单的代码(另有两个线程也在操作队列 q),我期望每隔 0.5 秒写一个’A’到队列中,但总是不能如愿:间隔时间有时会远远超过 0.5 秒。
原来,Queue.put()默认有 block = True 和 timeout 两个参数。当 block = True 时,写入是阻塞式的,阻塞时间由 timeout 确定。
当队列 q 被(其他线程)写满后,这段代码就会阻塞,直至其他线程取走数据。Queue.put()方法加上 block=False 的参数,即可解决这个隐蔽的问题。但要注意,非阻塞方式写队列,当队列满时会抛出 exception Queue.Full 的异常。
3.1 无法捕获 exception Queue.Empty 的异常
while True:
......
try:
data = q.get()
except Queue.Empty:
break
我的本意是用队列为空时,退出循环,但实际运行起来,却陷入了死循环。这个问题和上面有点类似:Queue.get()默认的也是阻塞方式读取数据,队列为空时,不会抛出 except Queue.Empty ,而是进入阻塞直至超时。 加上block=False 的参数,问题迎刃而解。
参考: