此文的前提是你会使用threading库,如果还不会使用,可以参考我之前写的threading库文章
Python的threading库的大致使用(细致解释,适合小白)
正文
queue模块通常与多线程,也就是threading库一起使用。
其作用是在多线程或多进程环境中安全地传递数据,或者说是一种常见的同步机制。
可以想象一下,此时有一个仓库,里面存放了十件货物,两位工人要将他们取出。对于人来说,会很自然的选择两人各自搬一件货物以此达到最大效率。但解释器不会这么做,他们不会自己进行工作排序,所以他们很可能会同时对一件货物进行搬送。
那么此时,我们就要告诉解释器该如何去把这十件货物最有效的搬送(执行)
此时我们就要用到queue模块。
下面对queue模块常用方法和概念进行介绍。
一:queue类的主要方法:
1:
put(item[,block[,timeout]]
put(item[, block[, timeout]])
# 将项目放入队列。如果队列已满且block为True(默认为True)——
#——则阻塞直到有空间可用或超时;如果block为False,则引发queue.Full异常。
# 其中,item: 这是必需的参数,表示要放入队列的项目
# block (可选): 这是一个可选的参数,如果指定为 True(默认值),则在队列已满的情况下,put 操作会阻塞,直到队列有空间可用。如果指定为 False,则在队列已满时会立即引发 queue.Full 异常。
# timeout (可选): 这是另一个可选参数,表示在阻塞模式下的超时时间,以秒为单位。如果队列已满并且 block 是 True,则最多等待 timeout 秒,如果在这个时间内队列有空间就放入项目;如果超时仍然没有空间,就引发 queue.Full 异常。如果没有指定 timeout,则会一直等待,直到队列有空间。
所以,put(item[, block[, timeout]])的调用方式可以有三种。
put(item)
# 使用默认值,即阻塞等待队列有空间。
put(item, block=False)
# 立即尝试放入,如果队列已满就引发异常
put(item, block=True, timeout=X)
# 阻塞等待队列有空间,最多等待 X 秒
2:
get([block[,timeout]])
get([block[, timeout]])
# 从队列中获取并移除一个项目。如果队列为空且block为True(默认为True)——
# ——则阻塞直到有项目可用或超时;如果block为False,则引发queue.Empty异常
# get(): 这是必需的参数,表示从队列中获取并移除一个项目。
# block (可选): 这是一个可选的参数。如果指定为 True(默认值),在队列为空的情况下,get 操作会阻塞,直到队列有项目可取。如果指定为 False,在队列为空时会立即引发 queue.Empty 异常
# timeout (可选): 这是另一个可选参数,表示在阻塞模式下的超时时间,以秒为单位。如果队列为空且 block 是 True,则最多等待 timeout 秒,如果在这个时间内队列有项目就获取;如果超时仍然没有项目,就引发 queue.Empty 异常。如果没有指定 timeout,则会一直等待,直到队列有项目。
同理,get([block[,timeout]]) 也有三种调用方式
get()
# 使用默认值,即阻塞等待队列有项目可取。
get(block=False)
# 立即尝试获取项目,如果队列为空就引发异常。
get(block=True, timeout=X)
# 阻塞等待队列有项目可取,最多等待 X 秒。
3:
qsize()
qsize()
# 返回队列中当前的项目数量
4:
empty()
empty()
# 如果队列为空,返回True;否则返回False
5:
full()
full()
# 如果队列已满,返回True;否则返回False
二:阻塞和非阻塞的介绍:
在使用put和get方法适合,可以通过block和timeout参数来控制阻塞行为。
如果block为T,也就是默认情况下,并且timeout为正值,则会最多等待timeout秒
如果timeout为负值则会一直等待下去,直到有项目或者空间可用
如果block设置为F,也就是False,那么将会立即引发queue.Full或者queue.Empty异常
三:特殊值
我们可以使用一些特殊的值,比如说:none来进行一些通知之类的操作
示例:
# 如果队列中获取到的项目名称是None,那么说明队列中已经没有任务需要处理了
# 那么就会执行break结束改线程的执行
if item is None:
break
......
# 向队列中加入一个特殊的值,用于通知某个线程退出
my_queue.put(None)
那么现在,根据我们所学,写一个能解决我们一开始提到的问题的脚本。
import queue
import threading
import time #模拟时间
# 定义工人(线程)的工作函数,或者说定义线程的执行逻辑
def worker(task_queue):
while True:
# 从任务队列中取出一个任务
task = task_queue.get()
# 如果取到的任务是None,表示任务结束,线程退出循环
if task is None:
break
# 模拟任务的执行时间
print(f"Worker is processing task for {task} seconds")
time.sleep(task)
if __name__ == "__main__":
# 创建一个任务队列
task_queue = queue.Queue()
# 添加任务到队列
tasks = [3, 1, 4, 1, 5, 9, 2, 6,5,3]
for task in tasks:
task_queue.put(task)
# 创建两个工人(线程)来执行任务
worker1 = threading.Thread(target=worker, args=(task_queue,))
worker2 = threading.Thread(target=worker, args=(task_queue,))
# 启动工人(线程)
worker1.start()
worker2.start()
# 等待所有任务完成
worker1.join()
worker2.join()
小结:通过queue模块,我们能更准确的告诉python解释器我们需要的执行逻辑与顺序,以此更为精确的达到我们的目的。
ps:上述脚本是个非常非常简单的示例脚本,而在实际使用中,queue模块会起到更大的作用,同时情景也会更加复杂,但总的来说,queue模块的使用并不难,只需要梳理好逻辑,然后准确的向python表达出来就好。