import asyncio
async def f1():
print('123')
await asyncio.sleep(1)
print('456')
return("f1完成")
async def f2():
print("abc")
#time.sleep()不能在async协程函数中使用, 因为它是time库提供的一个 同步阻塞 函数, 调用它会暂停整个程序的执行流.
#await -> 在 async 函数内部,要“等待”一个可等待对象完成,必须使用 await 关键字。
# await 关键字的作用就是暂停当前协程的执行,并将控制权交给事件循环,直到被 await 的可等待对象完成。
"""
await 它更广泛地应用于任何可能需要等待的、I/O 密集型的、异步操作,如:等待http请求返回响应时,
例如,等待 HTTP 请求返回响应、读写文件、网络连接、数据库查询等等,
这些操作的共同特点是:它们在执行过程中会有一段等待时间(等待数据从网络传输、等待硬盘读写等)。
#!也就是说 , 在async协程函数中, 不光是sleep这样的操作, 例如 等待http请求返回响应比较耗时的操作, 也可以用await标注, 将这个可能耗时的操作 在等待的时候 交给async去先执行其他 循环事件
"""
await asyncio.sleep(1)
print('def')
return("f2完成")
async def main():
#将 async协程对象封装为task对象
task1 = asyncio.create_task(f1())
task2 = asyncio.create_task(f2())
#asyncio.wait([func1(),func2[]]) 返回 已完成的任务集合 和 未完成的任务集合
#asyncio.wait() 是一个协程函数,它返回一个可等待对象。要让它实际执行并等待任务完成,必须在它前面加上 await 关键字。
done, pending = await asyncio.wait([task1,task2])
for task in done:
print (f"完成的:{task.result()}")
if pending:
print(f"未完成的:{pending}")
if __name__ == '__main__':
#loop = asyncio.get_event_loop() # 获取事件循环
#loop.run_until_comlete( result ) # 运行事件循环
# 在 python3.7 之后,可以省略上面两行,直接执行下面一行函数即可
asyncio.run(main())
刚开始接触 Python 里的 asyncio
觉得有点儿绕。现学现现记,现在就记录下学习 asyncio
时经常会遇到的几个地方,把一些概念弄清楚。
1. asyncio.sleep()
和 time.sleep()
有什么不同?
当你需要在 asyncio
的程序里让代码暂停一会儿时,可能会想到用 sleep
。但这里面有个小秘密:
time.sleep(1)
: 这是 Python 标准库里的一个函数。如果你在async
函数里用它,比如time.sleep(1)
,你的整个程序会完全停下来 1 秒。这就像按了程序的暂停键,其他所有的任务都得跟着一起等。在asyncio
里,我们希望的是多个任务能轮流执行,而不是互相等待。所以,time.sleep()
会阻塞asyncio
的“事件循环”,让并发失效。asyncio.sleep(1)
: 这个是asyncio
库专门提供的。当你写await asyncio.sleep(1)
时,你告诉程序:“我这个任务要等 1 秒,你先去看看有没有其他任务可以跑,等 1 秒到了再回来叫我。” 这样,asyncio
就可以在等待的这 1 秒里去处理其他任务,实现并发。它不会阻塞整个程序。
简单来说: 在 async
函数里,需要暂停的话,一定要用 await asyncio.sleep()
。
2. 为什么 asyncio.sleep()
前面要加 await
?
你可能会问,既然用了 asyncio.sleep()
,为什么前面还要加个 await
呢?
await
的作用:await
就像一个“等待”的信号。它告诉 Python,它后面跟着的操作(比如asyncio.sleep(1)
或者一个网络请求)可能需要一些时间才能完成。当程序遇到await
时,它会暂时停下当前任务,把控制权交给asyncio
的“事件循环”。事件循环就能趁这个机会去执行其他已经准备好的任务。等到之前被await
的操作完成了,事件循环再回来让这个任务继续执行。- 不加
await
会怎样? 如果你只写asyncio.sleep(1)
而不加await
,程序会创建一个“任务对象”,但这个任务对象并不会被真正地“等待”和执行。它就像你建了一个房子图纸,但没开始盖。你可能会看到一个警告,告诉你这个协程“从没被等待过”。而且,程序也不会真的暂停。
所以: 在 async
函数里,任何需要等待异步操作的地方,比如 asyncio.sleep()
或者发送网络请求,前面都必须加上 await
。
3. 在 async
函数里,除了 sleep
,还有哪些操作也需要 await
?
await
不仅仅是为了 sleep
。它的真正厉害之处在于处理那些耗时但又不需要一直占用 CPU 的操作。
想象一下,你的程序需要:
- 发送一个 HTTP 请求并等待响应: 比如从网站上下载数据。这会涉及到网络传输,需要等待服务器回应。
- 读取或写入文件: 当程序在硬盘上读写文件时,也需要等待。
- 查询数据库: 和数据库通信,等待查询结果。
这些操作都有一个共同点:它们在等待数据回来的时候,CPU 实际上是空闲的。如果用传统的方式,程序就会在那儿干等着。
在 asyncio
里,当你在这些操作前面加上 await
时(比如 await response.text()
或者 await aiofiles.open('file.txt').read()
),你的程序就会:
- 暂停当前任务。
- 把控制权交给
asyncio
事件循环。 - 事件循环会去执行其他准备好的任务。
- 等到网络数据回来、文件读写完成、数据库返回结果时,事件循环会再回到这个任务,让它继续往下执行。
这样,你的程序就可以在等待一个网络请求的同时,去处理另一个网络请求,或者做其他计算,大大提高了效率。这就像你同时开了好几个订单,不需要一个一个地等,而是可以同时处理。
记住: 在 async
函数里,凡是那些需要等待外部资源(比如网络、硬盘、数据库)的操作,我们都会使用 await
来配合相应的异步库(比如 aiohttp
处理 HTTP 请求,aiofiles
处理文件)。