Python的协程
本文最后更新于 2026年6月2日
未完待续
1.协程
协程(coroutine),又叫微线程或虚拟线程,是一种用户态的上下文切换技术,python中的协程,是通过一种可以被挂起,并且挂起后还能恢复执行的函数来实现的
某些操作会造成当前线程CPU发生阻塞,比如I/O操作,I/O操作不需要当前线程执行,但是当前线程提交了操作后也要白白等待一段时间拿到数据,才能继续执行,之前可以使用多线程来同时进行多路I/O操作,让子线程阻塞,主线程干些别的,而协程可以实现的就是让单个线程进行多路I/O操作时,也可以不需等待。
python中,协程可将多路I/O操作拆为多个方法,每个方法执行一路,事件循环实时监测各个方法状态,哪个方法阻塞了,事件循环迅速切换其他不阻塞的方法继续执行,而且这一切都发生在一个线程内。

2.async和协程函数
在python中,将一个普通函数加上async关键字,就会变成一个协程函数,执行协程函数,函数本身不会被调用,而是直接返回一个协程(coroutine )对象
async def a():
print('a')
return 1
async def b():
print('b')
return 2
task1 = a()
task2 = b()
if __name__ == "__main__":
print(task1)
print(task2)
<coroutine object a at 0x000001D4A91E5A80>
<coroutine object b at 0x000001D4A91E59C0>
<sys>:0: RuntimeWarning: coroutine 'a' was never awaited
<sys>:0: RuntimeWarning: coroutine 'b' was never awaited3.asyncio和await
要使得协程函数执行,需要asyncio.run()来调用协程函数,asyncio的原理,是将协程函数包装成任务,交给事件循环,事件循环就是一个死循环,不断轮询检查管理的协程函数的状态,发现谁阻塞了,立即将执行权拿走,交给其他未阻塞的协程函数继续执行当前线程
协程函数的返回值,也是从asyncio.run()的调用拿到
import asyncio
async def a():
print('a')
return 1
task1 = a()
if __name__ == "__main__":
r = asyncio.run(task1)
print(r)
a
1
<sys>:0: RuntimeWarning: coroutine 'b' was never awaitedawait对于python协程非常重要,关系着协程是否真正实现
事件循环基于await判断程序是否阻塞,遇到await后接的是需要等待的操作,CPU控制权便交给了事件循环,await后面的代码执行的同时,事件循环也会将执行权拿走,交给其他未阻塞的协程函数继续执行当前线程(如果有的话)
如果协程函数中没有await一个需要等待的操作,事件循环永远不会得到CPU控制权,当前函数一直执行下去,其他未阻塞的协程函数也不会得到执行权
await后面只能接可等待的对象:Future,其他协程函数,Task。
await只能出现在async修饰的函数中,这是python语法的规定。
例:协程函数中没有await一个需要等待的b(),b()执行完才会跳出继续执行a()
import asyncio
async def a():
print('a before')
await b()
print('a after')
return 1
async def b():
print('b')
return 2
task1 = a()
if __name__ == "__main__":
r = asyncio.run(task1)
print(r)
a before
b
a after
1例:协程函数中await了一个需要等待的b(),b()执行asyncio.sleep(5)模拟阻塞,但是因为事件循环中只是asyncio.run(task1)了一个task1任务,因此仍然是继续等待b()执行完成
import asyncio
async def a():
print('a before')
await b()
print('a after')
return 1
async def b():
print('b')
await asyncio.sleep(5)
return 2
task1 = a()
if __name__ == "__main__":
r = asyncio.run(task1)
print(r)
a before
b
a after
1例:多任务同步执行:协程方法main()中,启动其他三个协程方法,但是事件循环中只管理一个main(),因此三个协程方法a,b,c之一出现阻塞时,事件循环不会转移执行权到另两个。
import asyncio
async def a():
print('a')
await asyncio.sleep(5)
return 1
async def b():
print('b')
await asyncio.sleep(5)
return 2
async def c():
print('c')
await asyncio.sleep(5)
return 2
async def main():
task1 = a()
task2 = b()
task3 = c()
await task1
await task2
await task3
if __name__ == "__main__":
print('start')
asyncio.run( main() )
print('end')
start
a
b
c
end上述,都是不涉及事件循环切换任务的同步执行,实际场景使用更多的是涉及事件循环切换的异步执行
例:通过asyncio.create_task()方法,可以将协程函数包装为可以被事件循环调度的任务,并注册到事件循环中,main()和a(),b(),c()先后加入到事件循环
main()本身也是这个协程中的一环,其中三个函数执行或挂起,会导致它作为一个“容器”也被挂起,只有三个函数都执行完,main()才会继续
import asyncio
async def a():
print('a before')
await asyncio.sleep(5)
print('a after')
return 1
async def b():
print('b before')
await asyncio.sleep(5)
print('b after')
return 2
async def c():
print('c before')
await asyncio.sleep(5)
print('c after')
return 2
async def main():
task1 = asyncio.create_task( a() )
task2 = asyncio.create_task( b() )
task3 = asyncio.create_task( c() )
# 加入事件循环后,启动
v1 = await task1
v2 = await task2
v3 = await task3
print(v1)
print(v2)
print(v3)
if __name__ == "__main__":
print('start')
asyncio.run( main() )
print('end')start
a before
b before
c before
a after
b after
c after
1
2
2
end写一堆await总是很繁琐的,还可以通过asyncio.gather()将多个协程对象同时注入事件循环,并在全部执行完成后,一次性拿到所有结果
import asyncio
async def a():
print('a before')
await asyncio.sleep(5)
print('a after')
return 1
async def b():
print('b before')
await asyncio.sleep(5)
print('b after')
return 2
async def c():
print('c before')
await asyncio.sleep(5)
print('c after')
return 2
async def main():
res = await asyncio.gather(a(), b(), c())
print(res)
if __name__ == "__main__":
print('start')
asyncio.run( main() )
print('end')
start
a before
b before
c before
a after
b after
c after
[1, 2, 2]
end"如果文章对您有帮助,可以请作者喝杯咖啡吗?"
微信支付
支付宝