协程(Coroutine)是 Python 中一种用户态的并发机制,本质是可以“暂停和恢复执行”的函数。
| 模型 | 问题 |
|---|---|
| 同步 | 阻塞 |
| 线程 | 开销大 / 锁 |
| 协程 | ✅ 解决 I/O 并发 |
协程的目标是:用单线程实现高并发 I/O 密集型任务
其实我刚开始的时候,学习这里没有搞懂,其实我们看一段代码就知道了:
x1import asyncio2
3async def say_after(delay, what):4 await asyncio.sleep(delay)5 return f"{what} - {delay}"6
7async def main():8 task1 = asyncio.create_task(say_after(1, 'hello'))9 task2 = asyncio.create_task(say_after(2, 'world'))10
11 ret1 = await task112 ret2 = await task213
14 print(ret1)15 print(ret2)16
17asyncio.run(main())我们需要知道一点,就是一切用async def 定义的function 都是 Coroutine function,直接调用Coroutine funcion 的话,和generator一样,不会执行这个function里面的代码,而是会返回一个Coroutine对象。
那么我们就会想,如何去运行我们的Coroutine代码呢?一共有三种标准方式
asyncio.run()主要是 创建了一个事件循环(Event Loop) [后面会解释],执行协程,已经结束后关闭循环 三个作用。
在Coroutine内用 await(后面会说 await的作用)
创建任务: create_task 等等
在分析之前,我们还得介绍一下 什么叫Event Loop:
Event Loop(事件循环)是一个不断循环的调度器,负责管理和切换多个协程(Task)的执行。 这是有点官方的话了,但是我们可以把他理解为一个大脑,它现在面对很多任务,他负责管理和切换多个coroutine。
我们来分析一下这段代码的执行流程:
当我们运行asyncio.run(main())的时候,创建了一个 Event Loop ,并把 main() 这个Coroutine对象 包装成一个task,并注册到这个 Event Loop里面, 现在Event Loop一看里面的任务,只有一个main,那么就会执行这个main任务,当main任务执行到 task1的那个create_task()这一行的时候,会创建一个新的 coroutine: say_after(1, 'hello') ,并将这个coroutine 包装成一个Task(task1) ,把这个task1 注册到 Event Loop中,然后告诉 Event Loop说,task1 已经可以开始执行了,但是Event Loop现在没有办法执行,因为控制权还在main手里,那么main 现在趁着手上还有控制权,赶紧做了第二个create_task这个task2,同样也是告诉main,这里还有个新的task哦,叫作[say_after(2,'world')],也可以开始运行了,在这里之后,他才开始进行await task1 和 await task2, 那么main遇到await task1 的时候,就会告诉 Event Loop, 我现在需要等task1 完成了,我才能接着执行,所以把控制权又给了Event Loop,Event Loop就会去执行task1,task1 跟 Event Loop说,我需要一秒才能执行完毕,Event Loop 闲来无事就发现,这里有一个task2可以执行,于是他就执行了task2, task2 跟他说,我需要两秒才能完成,这样两个task就可以同时进行等待了。
以上就是大概的执行流程,毕竟这个写的有点绕,有点糊涂了可能。。。
总结就是:协程不会被强制打断,只有在 await 时才会主动让出控制权,这种机制称为“协作式调度”。
协程的本质是:通过 await 主动让出执行权,由事件循环调度,从而在单线程中实现高并发 I/O。