Python3 queue 模块

Python3 的 queue 模块提供了线程安全的队列数据结构,用于在多线程环境中实现线程间的通信和数据共享。queue 模块实现了三种类型的队列,它们的主要区别在于元素的获取顺序。

模块概览

queue 模块定义了以下三个类:
 
  1. queue.Queue(maxsize=0):
    • FIFO (先进先出) 队列。
    • 元素按照添加顺序排列,获取时从队列头部取出。
    • maxsize 参数指定队列的最大容量。如果 maxsize 小于或等于 0,队列容量是无限的。当队列满时,put() 操作会阻塞,直到有空间可用。
  2. queue.LifoQueue(maxsize=0):
    • LIFO (后进先出) 队列,也称为栈(Stack)。
    • 元素按照添加顺序的逆序排列,获取时从队列尾部(最新添加的元素)取出。
    • maxsize 参数用法同上。
  3. queue.PriorityQueue(maxsize=0):
    • 优先级队列
    • 元素被赋予一个优先级,队列会确保每次取出的都是优先级最低(或最高,取决于比较方式)的元素。
    • 通常,元素应该是一个元组 (priority, data),其中 priority 是一个可比较的数字(越小表示优先级越高)。
    • maxsize 参数用法同上。

核心方法

这三个队列类都提供了相似的 API,以下是最常用的方法:
 
  1. q.put(item, block=True, timeout=None):
    • 将 item 放入队列。
    • 如果 block=True(默认)且队列已满,此方法会阻塞,直到队列中有空间。
    • 如果 timeout 是一个正数,它会阻塞最多 timeout 秒。如果在超时时间内仍无法放入,会抛出 queue.Full 异常。
    • 如果 block=False,队列满时会立即抛出 queue.Full 异常。
  2. q.get(block=True, timeout=None):
    • 从队列中取出并返回一个元素。
    • 如果 block=True(默认)且队列为空,此方法会阻塞,直到队列中有元素。
    • 如果 timeout 是一个正数,它会阻塞最多 timeout 秒。如果在超时时间内仍无法取出,会抛出 queue.Empty 异常。
    • 如果 block=False,队列为空时会立即抛出 queue.Empty 异常。
  3. q.qsize():
    • 返回队列中当前的元素数量。注意,这只是一个近似值,因为在多线程环境中,队列的大小可能在调用此方法和使用返回值之间发生变化。
  4. q.empty():
    • 如果队列为空,返回 True,否则返回 False。同样,这也是一个近似值。
  5. q.full():
    • 如果队列已满,返回 True,否则返回 False。这也是一个近似值。
  6. q.task_done():
    • 此方法用于表示队列中的一个任务已经完成。
    • 它通常与 q.join() 一起使用。每个从 get() 获取的任务,最终都应该调用一次 task_done()
  7. q.join():
    • 阻塞调用线程,直到队列中的所有任务都被处理完毕。
    • 也就是说,直到每一个通过 put() 放入的元素都被 get() 取出并对其调用了 task_done()

使用示例

1. 基本的 FIFO 队列 

import queue

# 创建一个无限容量的队列
q = queue.Queue()

# 向队列中添加元素
q.put('apple')
q.put('banana')
q.put('cherry')

print(f"队列大小: {q.qsize()}")  # 输出: 队列大小: 3

# 从队列中取出元素
print(q.get())  # 输出: apple
print(q.get())  # 输出: banana

print(f"队列大小: {q.qsize()}")  # 输出: 队列大小: 1

print(q.get())  # 输出: cherry

# 如果队列已空,get() 会阻塞
# print(q.get()) # 程序会在这里卡住,直到有新元素被放入
 

2. 多线程示例 (生产者 - 消费者模式)

这是 queue 模块最经典的应用场景。
  
import queue
import threading
import time
import random

# 创建一个最多容纳5个元素的队列
buffer = queue.Queue(maxsize=5)

def producer():
    """生产者线程: 向队列中放入数据"""
    for i in range(10):
        item = f"产品{i}"
        try:
            # 尝试将产品放入队列,如果队列满了,等待1秒
            buffer.put(item, block=True, timeout=1)
            print(f"生产者 {threading.current_thread().name} 生产了: {item}")
        except queue.Full:
            print(f"队列已满,生产者 {threading.current_thread().name} 无法生产 {item}")
        
        # 模拟生产耗时
        time.sleep(random.uniform(0.1, 0.5))

def consumer():
    """消费者线程: 从队列中取出数据并处理"""
    while True:
        try:
            # 尝试从队列中取出产品,如果队列空了,等待2秒
            item = buffer.get(block=True, timeout=2)
            print(f"消费者 {threading.current_thread().name} 消费了: {item}")
            
            # 模拟消费耗时
            time.sleep(random.uniform(0.5, 1))
            
            # 通知队列该任务已完成
            buffer.task_done()
            
        except queue.Empty:
            print(f"队列已空,消费者 {threading.current_thread().name} 退出")
            break

if __name__ == "__main__":
    # 创建并启动生产者线程
    producer_thread = threading.Thread(target=producer, name="P1")
    producer_thread.start()

    # 创建并启动两个消费者线程
    consumer_thread1 = threading.Thread(target=consumer, name="C1")
    consumer_thread2 = threading.Thread(target=consumer, name="C2")
    consumer_thread1.start()
    consumer_thread2.start()

    # 等待生产者线程完成所有生产任务
    producer_thread.join()
    print("所有生产者任务已完成")

    # 等待队列中所有任务被消费掉
    buffer.join()
    print("所有队列中的任务已被消费")

    # 消费者线程在检测到队列为空后会自行退出,这里等待它们结束
    consumer_thread1.join()
    consumer_thread2.join()

    print("所有线程结束")
 
 
代码解析:
 
  • 生产者线程 producer 不断生成产品并放入队列 buffer。当队列满时 (maxsize=5),put() 会阻塞或超时。
  • 消费者线程 consumer 不断从队列中取出产品进行 “消费”。消费完成后,必须调用 buffer.task_done() 来标记任务完成。
  • 主线程使用 producer_thread.join() 等待生产者完成。然后使用 buffer.join() 等待队列中所有已存在的任务都被消费掉。最后等待消费者线程结束。

3. 优先级队列 (PriorityQueue) 

import queue

# 创建一个优先级队列
pq = queue.PriorityQueue()

# 放入元素,元素是 (优先级, 数据) 的元组
# 数字越小,优先级越高
pq.put((3, '任务 C'))
pq.put((1, '任务 A'))
pq.put((2, '任务 B'))
pq.put((1, '任务 A-1')) # 相同优先级的任务,按FIFO顺序取出

print("优先级队列中的任务将按优先级取出:")
while not pq.empty():
    priority, task = pq.get()
    print(f"处理优先级 {priority} 的任务: {task}")
    pq.task_done()

# 输出顺序:
# 处理优先级 1 的任务: 任务 A
# 处理优先级 1 的任务: 任务 A-1
# 处理优先级 2 的任务: 任务 B
# 处理优先级 3 的任务: 任务 C
 

总结

queue 模块是 Python 中进行线程同步和数据传递的强大工具。
 
  • 线程安全queue 模块的所有操作都是线程安全的,这意味着多个线程可以同时调用其方法而不会导致数据损坏。
  • 阻塞机制put() 和 get() 方法的阻塞特性使得线程可以高效地等待资源,而无需使用 time.sleep() 进行轮询,从而节省 CPU 资源。
  • 适用场景: 最适合实现生产者 - 消费者模式,例如处理任务队列、数据 pipelines 等。

posted on 2025-11-24 14:35  小陶coding  阅读(7)  评论(0)    收藏  举报