Python标准库-queue模块
queue是Python标准库中的线程安全队列,常用于多线程编程时
1. queue简介
假设程序中有两个线程:
1 | 线程 A:不断产生任务 |
如果只使用一个普通列表,则需要考虑锁、竞态条件、等待通知等等
而queue则可以实现上述功能,并且可以更加灵活
queue是Python标准库
可以直接:
1 | import queue |
queue中主要有四种队列类:
1 | Queue |
最常用的是Queue,它是一个先进先出(FIFO)的队列
所以我们常用的导入方式为:
1 | from queue import Queue,Empty,Full |
Empty和Full是queue中定义的异常类
Empty是队列为空时抛出的异常
Full是队列满时抛出的异常
2. Queue():创建队列
函数签名:
1 | queue.Queue(maxsize=0) |
基本使用:
1 | from queue import Queue |
3. Queue.put():向队列中添加元素
函数签名:
1 | Queue.put(item, block=True, timeout=None) |
block:如果队列满了,则是否阻塞。如果为True,则当队列满时,会阻塞线程,直到队列出现空位;如果为False,队列满了则会抛出Full异常
timeout:如果block为True,则表示阻塞的时间,单位为秒
例子:
1 | import queue |
4. Queue.get():从队列中取出元素
函数签名:
1 | Queue.get(block=True, timeout=None) |
block:如果队列为空,则是否阻塞。如果为True,则当队列为空时,会阻塞线程,直到队列出现元素;如果为False,队列为空则会抛出Empty异常
timeout:如果block为True,则表示阻塞的时间,单位为秒
例子:
1 | import queue |
5. Queue.get_nowait():从队列中取出元素,不阻塞
get_nowait()方法与get()方法类似,但是不会阻塞线程,如果队列为空,则会抛出Empty异常
基本等价于:
1 | q.get(block=False) |
例子:
1 | from queue import Queue, Empty |
6. Queue.put_nowait():向队列中添加元素,不阻塞
put_nowait()方法与put()方法类似,但是不会阻塞线程,如果队列满了,则会抛出Full异常
基本等价于:
1 | q.put(item, block=False) |
7. Queue.empty():返回队列是否为空
empty()方法返回队列是否为空
例如:
1 | from queue import Queue, Empty |
empty()返回的是一个布尔值,如果队列为空,则返回True,否则返回False
但是多线程程序中,empty()并不完全可靠,因为可能empty()返回结果后,其他线程继续操作队列,导致队列状态与empty()的结果不一致。
因此,建议使用get_nowait()或get()方法,而不是empty()方法
8. Queue.full():返回队列是否满
full()方法返回队列是否满
例如:
1 | from queue import Queue, Full |
full()返回的是一个布尔值,如果队列满,则返回True,否则返回False
与empty()类似,多线程环境中它只能返回调用那一刻的结果,不能保证下一条语句执行时状态没变化
因此,建议使用put_nowait()或put()方法,而不是full()方法
9. Queue.qsize():返回队列中元素的数量
qsize()方法返回队列中元素的数量
例如:
1 | from queue import Queue |
输出:
1 | 0 |
同样,多线程环境中,qsize()方法只能返回调用那一刻的结果,不能保证下一条语句执行时状态没变化
10. Queue.task_done():通知线程完成一个任务
task_done()方法通知线程完成一个任务
例如:
1 | data = q.get() |
只表示获取一个元素,但是并不表示完成一个任务
1 | q.task_done() |
表示完成一个任务
例子:
1 | def worker(): |
11. Queue.join():等待队列中所有线程结束
join()方法等待队列中所有线程结束
join()和task_done()方法常常配合使用
例如:
1 | q.put(1) |
等待队列中的所有任务全部被处理完成,也就是等待队列中所有任务都调用了task_done()方法
完整例子:
1 | import threading |
简单理解:
1 | put() |
如果任务完成后没有调用task_done()方法,Queue也不会阻止获取下一个任务,但Queue内部依旧会认为上一个任务没有完成,也就是说这种情况下调用join()方法程序会一直阻塞
所以在实际任务中,每一个任务最好都对应一个task_done()方法,并且在任务完成后调用task_done()方法