Python异步编程
什么是异步,为什么是异步?
异步,意指你不用等到某个条件达成再去做某事,比如:你不用等到一切都准备好了才开始跳槽🐕
.那么在 多线程
存在的情况下,我们为什么还要把目光投向 异步
呢?假如我们使用 threading
或者 _thread
编写多线程代码,实际上我们还要承受 CPU
切换线程的代价,因为 单线程
的缘故.此外我们在使用多线程的时候,还会面临资源的竞争
、锁
、线程池充分利用
……诸如此类的问题.
什么是Python异步编程
- 从Python3.5开始,python3开始支持异步编程.
- 在python中我们会用到
asyncio
包来进行异步编程. - 从
asyncio
这个报名可以得知,在python中我们进行异步编程时,我们是围绕IO网络性能
进行,而不是CPU操作
. - 体验地道、现代的python异步编程,我建议python版本应该>=python3.10
- 我们主要使用异步生成的方式来进行异步编程,即
async def
,在方法里面使用 yield 的生成器函数。返回一个提供__anext__ 方法(获取下一项)的异步生成器对象。
为什么是Python3.10
主要是因为从3.10版本开始,python3.7中的asyncio.get_event_loop
已被弃用,最终变成了3.10版本asyncio.get_running_loop
,直接一步到位,免去升级烦恼。
开始异步编程第一步
引入 asyncio
import asyncio
认识关键字
1. async
async def 定义一个协程函数
2. await
await exp await 表达式不阻塞
3. 简单示例 1
import asyncio
async def foo(name: str):
print(f"foo is {name}")
async def hi():
await foo("one")
print("It's hi func")
if __name__ == '__main__':
asyncio.run(hi())
------------------------------
foo is one
It's hi func
从上述例子中我们能看到,我们使用async def
定义了协程函数
,用了await
关键字委托另一个原生协程, asyncio.run
启动事件循环,仅当事件循环退出后返回。在使用asyncio 的脚本经常这样做,即把 hi 实现为协程,在 if__name__ == '__main__':
块中使用 asyncio.run 驱动。
4. 简单示例 2
import asyncio
import time
async def foo(n):
await time_cost() # wait 5s before continuing
print(f"n: {n}!")
async def time_cost():
await asyncio.sleep(5)
async def main():
start = time.time()
tasks = [foo(1), foo(2), foo(3)]
await asyncio.gather(*tasks)
end = time.time()
print(end-start)
asyncio.run(main())
----------------------------------------------
n: 1!
n: 2!
n: 3!
5.0098652839660645
从上述例子中我们能看到,我们使用了asyncio.sleep
暂停协程的执行一段时间,所以前后差是5S
,假如我们在time_cost
里面使用time.sleep(5)
,那么前后差应该是多少?,答案是15S
.因为当调用time.sleep(5)
时,它会阻塞整个脚本的执行,使其暂停,什么也不做。但是当你调用await asyncio.sleep(5)
时,它会请求事件循环运行其他任务,同时等待语句完成执行。此外我们还使用了asyncio.gather
:来获取一系列 awaitables
,返回成功等待的值的聚合列表。
5. 简单示例 3
import asyncio
import time
async def foo(n):
await time_cost() # wait 5s before continuing
print(f"n: {n}!")
return n
async def time_cost():
await asyncio.sleep(5)
async def main():
start = time.time()
tasks = [foo(1), foo(2), foo(3)]
for coro in asyncio.as_completed(tasks):
num = await coro
print(num)
end = time.time()
print(end-start)
if __name__ == '__main__':
asyncio.run(main())
--------------------------------------------
n: 1!
n: 3!
n: 2!
1
3
2
5.009920835494995
在上述例子中,asyncio.as_completed
是一个生成器,负责产出协程,按照传入的协程完成的顺序(不是协程的提交顺序)返回结果,所以我们看到结果是1、3、2而不是1、2、3。as_completed
还有一个作用就是告诉我们,协程已经结束咧。因此我们在用await
获取结果的时候是不阻塞的。