Python 同步原语与队列
锁、事件与队列协调并发任务;优先用队列传递所有权,降低共享内存复杂度。
#type / concept
#status / growing
#tech / dev
#resource / python
#tech / lang / python
[!info] 关联笔记
Python 同步原语与队列
这个概念为什么出现
多线程/多协程共享计数器、连接池、缓冲时,需要协调。锁能保临界区;队列常常更简单:把数据所有权在任务间传递,减少共享写。
[!abstract] 一句话理解 同步原语保护共享状态;队列实现生产者-消费者解耦。能队列就别到处加锁。
最小可运行示例
先把示例放进业务场景,再看代码:
场景:异步爬虫:生产者放 URL,消费者写结果
# async_queue_demo.py
# 业务意图:队列传递任务,避免共享 list 乱追加。
# 教学点:
# - asyncio.Queue;
# - worker 消费;
# - join 等待完成。
import asyncio
async def worker(name: str, q: asyncio.Queue[str], out: list[str]) -> None:
while True:
url = await q.get()
try:
await asyncio.sleep(0.05)
out.append(f"{name}:{url}")
finally:
q.task_done()
async def main() -> None:
q: asyncio.Queue[str] = asyncio.Queue()
out: list[str] = []
workers = [asyncio.create_task(worker(f"w{i}", q, out)) for i in range(2)]
for u in ["u1", "u2", "u3"]:
await q.put(u)
await q.join()
for w in workers:
w.cancel()
print(sorted(out))
if __name__ == "__main__":
asyncio.run(main())
建议运行:
python async_queue_demo.py
期望输出(worker 分配可能不同,集合应一致):
['w0:u1', 'w0:u3', 'w1:u2']
或等价排列。
结合场景再看三个关注点
- Queue 自带协调,减少手写条件变量。
- task_done/join 表达“任务排空”。
- 线程世界对应
queue.Queue。
核心概念与准确模型
| 原语 | 场景 |
|---|---|
| Lock/Mutex | 临界区 |
| RLock | 可重入 |
| Event | 一次性信号 |
| Semaphore | 限流 |
| Queue | 传递工作项 |
边界情况与反直觉行为
- 锁粒度太大吞吐下降。
- 未 join 就退出丢任务。
- 异步锁与线程锁不能混用错环。
常见误区
[!warning] 常见误区:所有共享都靠全局 list+锁 错误理解:唯一模式。
正确模型:优先所有权转移(队列)与不可变消息。
工程实践
- 有界队列做背压
- 超时获取避免永久阻塞
- 指标:队列深度
本节总结
同步工具保护正确性;队列简化架构。并发代码的可读性往往来自“少共享”。
自测题
- 为何队列能减少锁?
task_done的作用?
参考答案
- 数据所有权随消息移动,降低同时写共享结构。
- 标记单条任务处理完成,使
join可等待排空。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| asyncio.Queue | 文档 | 异步队列 |
| threading | 文档 | 线程原语 |