《同名不同魂:一文吃透 Python 的 `Queue` 与 `asyncio.Queue`(从入门到工程化最佳实践)》

《同名不同魂:一文吃透 Python 的 Queueasyncio.Queue(从入门到工程化最佳实践)》

面向对象:既在学并发基础的初学者,也在生产环境里追求稳定与吞吐的资深开发者
目标:讲清两者的设计哲学、使用边界与工程实践,配齐可复制的代码模板与踩坑清单


开篇引入:为什么这两个“队列”老被搞混?

Python 自 1991 年问世,以简洁语法丰富生态成为 Web、数据工程与 AI 的“胶水语言”。在并发世界里,“队列”是最常用的解耦工具。但很多同学第一次接触并发时,会把 queue.Queue(下文简称 Queue)与 asyncio.Queue(下文简称 AQueue)当作“同物两名”。结果不是阻塞住事件循环,就是在多线程里糟糕地混用协程原语。

这篇文章从语言精要 → 高级实践 → 工程化落地,系统讲清两者的差异、适用场景与最佳实践。读完你将能回答:

  • 我该在阻塞 I/O 的线程世界用 Queue,还是在协程世界用 AQueue?
  • 如何做背压、限流、超时与优雅关停
  • 如何在同一项目中混合使用线程/协程/进程而不踩坑?

1. 概念速写:同是队列,语义不同

维度 queue.Queue(线程队列) asyncio.Queue(协程队列)
运行模型 多线程/同步阻塞 单线程/事件循环/协作式调度
API 风格 put()/get() 阻塞;put_nowait()/get_nowait() 非阻塞 await put()/await get() 协程挂起;put_nowait()/get_nowait() 非阻塞
背压与容量 maxsize + 线程阻塞 maxsize + await 等待,不阻塞线程
取消/超时 get(timeout=...)Empty;取消靠线程间协议 asyncio.wait_for()/Task.cancel() 原生支持取消
线程安全 线程安全 线程安全对协程而言无意义;不是跨线程安全
使用场景 阻塞库(如 requests、磁盘 I/O)、生产者—消费者(线程) 海量并发 I/O(aiohttpasyncpg、WebSocket 等)
常见坑 在 CPU 密集任务上开太多线程;无界队列内存膨胀 在协程里使用阻塞函数导致卡 loop;跨线程误用 AQueue

一句话

  • Queue 服务于线程/同步世界,通过阻塞协调背压;
  • AQueue 服务于协程/异步世界,通过await 挂起实现背压与高并发。

2. 基础部分:语法与行为对照

2.1 线程队列最小可用实现(I/O 密集)

import queue, threading, time, random

def producer(q: queue.Queue, n=1000):
    for i in range(n):
        q.put(i)                 # 满了就阻塞,形成背压
    q.put(None)                   # 哨兵,通知消费者结束

def consumer(q: queue.Queue, results: list):
    while True:
        x = q.get()               # 空则阻塞等待
        if x is None:
            q.put(None)           # 传递哨兵给其他消费者
            q.task_done()
            break
        time.sleep(random.uniform(0.005, 0.01))  # 模拟 I/O
        results.append(x * 2)
        q.task_done()

def main():
    q = queue.Queue(maxsize=1000)
    results = []
    ts = [threading.Thread(target=consumer, args=(q, results)) for _ in range(8)]
    for t in ts: t.start()
    producer(q, 5000)
    q.join()                      # 等所有任务 task_done
    for t in ts: t.join()
    print(len(results))

if __name__ == "__main__":
    main()

2.2 异步队列最小可用实现(海量连接)

import asyncio, random, aiohttp

async def producer(q: asyncio.Queue, urls):
    for u in urls:
        await q.put(u)            # 满了就 await,事件循环可继续跑其他任务
    await q.put(None)             # 哨兵

async def fetch(session, url):
    async with session.get(url, timeout=aiohttp.ClientTimeout(total=5)) as r:
        return url, r.status

async def consumer(q: asyncio.Queue, results: list, sem: asyncio.Semaphore):
    async with aiohttp.ClientSession() as s:
        while True:
            u 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铭渊老黄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值