LangChain-asyncio异步编程
·
“同一线程内的任务交替执行”是异步编程(如 asyncio)的核心特点,指的是:在单个线程里,多个任务(比如多个异步请求、操作)不会“同时运行”,而是“你执行一会儿,我执行一会儿”,通过主动“让出”线程控制权来实现“看似同时进行”的效果。
我们用一个生活例子+代码演示,彻底讲清楚:
生活例子:“单线程”就像一个人干多件事
假设你(相当于“单个线程”)同时要做两件事:
- 烧开水(相当于
astream()等待模型返回chunk,是“I/O 等待”——不需要一直盯着,水开需要时间) - 回复消息(相当于另一个异步任务,需要主动操作)
你的操作流程会是:
- 先把水壶放上去烧(启动任务1),然后不盯着水壶,转身去回复消息(执行任务2)。
- 回复完一条消息,回头看看水开了没(检查任务1是否有结果)。如果没开,继续回复下一条消息(任务2继续)。
- 直到水壶响了(任务1有结果:水开了),你才停下回复消息,去处理开水(任务1继续执行)。
整个过程中,你(单个线程)始终在交替处理两个任务,没有同时做两件事,但因为“等待烧开水时没有闲着”,最终看起来像“同时完成了两件事”。
代码演示:单线程内的任务交替
用 asyncio 模拟上面的场景(两个任务在单线程内交替执行):
import asyncio
import time
# 任务1:模拟"烧开水"(I/O等待,不占用CPU)
async def boil_water():
print("开始烧开水...(等待3秒)")
await asyncio.sleep(3) # 模拟I/O等待(主动让出线程)
print("水开了!")
# 任务2:模拟"回复消息"(需要主动执行)
async def send_messages():
for i in range(3):
print(f"回复第{i+1}条消息")
await asyncio.sleep(1) # 每条消息间隔1秒(主动让出线程)
# 主函数:同时启动两个任务
async def main():
# 启动两个任务(注意:此时任务还未执行,只是加入事件循环)
task1 = asyncio.create_task(boil_water())
task2 = asyncio.create_task(send_messages())
# 等待两个任务都完成
await task1
await task2
# 在当前线程启动事件循环(单线程)
asyncio.run(main())
执行结果(单线程内的交替过程):
开始烧开水...(等待3秒) # 0秒:任务1启动,执行await sleep(3)后主动让权
回复第1条消息 # 0秒:事件循环调度任务2,执行第一段后await sleep(1)让权
(等待1秒,0秒~1秒) # 事件循环等待,直到任务2的1秒结束
回复第2条消息 # 1秒:任务2等待结束,执行第二段后await sleep(1)让权
(等待1秒,1秒~2秒) # 事件循环等待,直到任务2的1秒结束
回复第3条消息 # 2秒:任务2等待结束,执行第三段后await sleep(1)让权
(等待1秒,2秒~3秒) # 事件循环等待,3秒时任务1的3秒等待先结束
水开了! # 3秒:任务1等待结束,执行完毕
核心分析:
1. 任务主动“让出线程控制权”
send_messages() 函数在打印“回复第1条消息”后,执行到 await asyncio.sleep(1):
- 这行代码的含义是“等待1秒,但在此期间不占用线程”。
- 此时,
send_messages()任务会主动告诉事件循环(单线程的调度中心):“我需要等待1秒,现在把线程控制权让给你,你可以去执行其他就绪的任务”。
2. 事件循环切换到其他任务
但是 boil_water() 任务(处于“等待3秒”的状态中,已经让权)。
3. 1秒后,任务被唤醒继续执行
当 asyncio.sleep(1) 的1秒等待结束后:
- 事件循环会收到“
send_messages()的等待结束”信号,将send_messages()标记为“就绪状态”。 - 事件循环切换回
send_messages()任务,从await asyncio.sleep(1)之后继续执 - 于是会打印“回复第2条消息”,然后再次执行
await asyncio.sleep(1),重复上述“让权→等待→唤醒”的过程。
关键细节:为什么能“交替”?
- 核心是
await关键字:当任务执行到await(比如await asyncio.sleep()或await model.astream())时,会主动**“让出线程控制权”**,告诉事件循环:“我现在要等,你让其他任务先跑”。 - 事件循环(单线程内的调度器)会记录每个任务的状态,当一个任务“让出”后,就切换到另一个“就绪”的任务执行。
- 直到被
await的操作完成(比如sleep(3)结束、模型返回chunk),事件循环才会让原任务继续执行。
对比多线程的“并行”:
如果用多线程,相当于“两个人同时干活”:一个人专门烧开水(哪怕盯着等),另一个人专门回复消息——两人真正“同时进行”,不需要“交替”。
而异步的“交替执行”是一个人干多件事,通过“不浪费等待时间”来提高效率,本质还是“串行”,但看起来像“并行”。
总结
“同一线程内的任务交替执行”是异步编程的核心逻辑:
- 单个线程,多个任务。
- 任务执行到
await(等待 I/O 时)会主动“让权”。 - 事件循环负责切换任务,让线程始终“有事干”。
- 最终效果:不阻塞线程,高效利用时间,看似“同时处理多个任务”。
- await asyncio.sleep(1)(以及所有带 await 的异步操作),是任务向事件循环 “主动报备状态” 的关键信号,事件循环正是通过这些 await 信号,才能清晰地知道 “哪些任务在干活、哪些任务在等待、该怎么管理所有任务”。
这也是异步比同步(一个任务阻塞就全卡住)高效的原因~
更多推荐



所有评论(0)