前言
我们前面讲了进程、线程,今天引入另外一个概念协程
协程
先上一下结论哈,普通函数前面加了一个async就是协程
示例代码
python
#使用async来修饰普通函数
async def main():
print("test")
return "test"
if __name__ == "__main__":
print(type(main()))
打印结果
我们先不管这个报错,先看print的type数据 打印出来的 <class 'coroutine'> ,这个就是协程的意思
如何执行协程函数
python
async def main():
print("test")
return "test"
if __name__ == "__main__":
print(type(main()))
main()
我在上面代码的基础上加了一个 main()方式看下能否像普通函数一样去执行
打印结果

发现除了打印了类型外main执行直接就报错了: RuntimeWarning: coroutine 'main' was never awaited main() RuntimeWarning: Enable tracemalloc to get the object allocation traceback 报错的信息是协程没有使用await ,那我们加上await
引入await
python
async def test():
print("test")
return "test"
async def main():
await test()
if __name__ == "__main__":
print(type(main()))
main()
我们代码进行了一次转换一开始main的功能放到了test函数里面,又额外定义了一个main来await新的test
打印结果

发现还是报和刚才一样的错误,这是因为main()这个协程函数执行的时候也没加await ,但是await有必须得在async定义的协程函数里 ,这个if怎么加async呢
引入asyncio
python
import asyncio
async def test():
print("test")
return "test"
async def main():
await test()
if __name__ == "__main__":
print(type(main()))
asyncio.run(main())
打印结果

结论
- async定义的普通函数就是协程函数
- 协程函数执行前面必须加await
- async和await相互依存,相互依赖
多个协程
刚才上面代码是单个协程执行时候的场景,但是如果我们存在多个协程执行点时候呢?
示例代码
python
import asyncio
async def test(name: str, age: int):
print("name{}, age{}".format(name, age))
return f"当前协程是{name},年龄{age}"
async def main():
print("task1开始执行")
task1 = asyncio.create_task(test("小刘", 28))
print("task2开始执行")
task2 = asyncio.create_task(test("小孔", 38))
result1 = await task1
print("await task1执行结束")
result2 = await task2
print("await task2执行结束")
print("result1执行结果是:", result1)
print("result2执行结果是:", result2)
if __name__ == "__main__":
asyncio.run(main())
代码解释:我们通过create_task创建了多个要执行的协程任务(task)然后在分别获取每个协程的结果
打印结果

我们发现再打印await task1执行结束结束前,协程task1已经执行结束,task2也是这样的,所以我们得出结论
结论
-create_task创建协程任务的时候,就会开始执行协程了 -执行协程不会阻塞主任务 -碰到await会阻塞当前的协程
执行流程
text
asyncio.run(main())
│
▼
事件循环启动
│
▼
执行 main 协程
│
▼
create_task(test1) ──────────────────┐
create_task(test2) ──────────────────┤
│
▼
事件循环调度队列
[task1, task2]
│
▼
┌─────────────────────────────────────────────────────┐
│ 事件循环开始调度 │
└─────────────────────────────────────────────────────┘
│
┌────────────────────────┴────────────────────────┐
▼ ▼
╔════════════════════╗ ╔════════════════════╗
║ 执行 test1 ║ ║ 执行 test2 ║
║ │ ║ ║ │ ║
║ [遇到await] ║ ║ [遇到await] ║
║ │ ║ ║ │ ║
║ 交出CPU控制权 ║ ║ 交出CPU控制权 ║
╚════════════════════╝ ╚════════════════════╝
│ │
└──────────────────┬───────────────────────────────┘
▼
┌─────────────────────┐
│ 事件循环重新调度 │
│ 检查谁可以继续执行 │
└─────────────────────┘
│
┌──────────────────┴──────────────────┐
│ │
▼ ▼
╔════════════════════╗ ╔════════════════════╗
║ 被事件循环唤醒 ║ ║ 被事件循环唤醒 ║
║ │ ║ ║ │ ║
║ ▼ ║ ║ ▼ ║
║ test2执行完成 ║ ║ test1执行完成 ║
╚════════════════════╝ ╚════════════════════╝
│ │
└──────────────────┬───────────────────┘
▼
┌─────────────────────┐
│ 所有任务执行完成 │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ 返回 main 协程 │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ 关闭事件循环 │
└─────────────────────┘
关键点
- 遇到await:协程主动交出控制权给事件循环
- 睡眠期间:协程被挂起,不占用CPU
- 唤醒机制:时间到了,事件循环把协程重新放回就绪队列
- 调度顺序:谁先醒来谁先执行(test2先醒,所以先结束)
引入gather
python
import asyncio
async def test(name: str, age: int):
print("name{}, age{}".format(name, age))
return f"当前协程是{name},年龄{age}"
async def main():
print("task1开始执行")
task1 = asyncio.create_task(test("小刘", 28))
print("task2开始执行")
task2 = asyncio.create_task(test("小孔", 38))
# gather 返回一个 awaitable 对象
result = await asyncio.gather(task1, task2)
for task in result:
print(task)
if __name__ == "__main__":
asyncio.run(main())
示例结果

引入as_completed
python
import asyncio
async def test(name: str, age: int):
print("name{}, age{}".format(name, age))
return f"当前协程是{name},年龄{age}"
async def main():
print("task1开始执行")
task1 = asyncio.create_task(test("小刘", 28))
print("task2开始执行")
task2 = asyncio.create_task(test("小孔", 38))
for coro in asyncio.as_completed([task1, task2]):
result = await coro
print(result)
if __name__ == "__main__":
asyncio.run(main())
示例结果

gather和as_completed的区别
