如果你想了解異步編程,那么必然會涉及出許多相關概念。
- 堵塞/非堵塞
- 同步/異步
- 多進程/多線程/協程
為什么我要學習這個話,因為我想搞懂異步框架和異步接口的調用。所以,我的學習路線是這樣的:
1.python異步編程
2.python Web異步框架(tornado/sanic)
3.異步接口調用(aiohttp/httpx)
那么異步編程有什么好處?在某些場景下它可以提高性能。我們知道CPU的速度快於磁盤、網絡等IO。一旦遇到IO操作,如讀寫文件、發送網絡數據時,就需要等待IO操作完成,才能進行下一步操作。這種情況稱為同步IO。我們可以使用多線程來解決這類問題,另一種方式是通過異步。
python在3.4版本引入asyncio,到 3.5版本又加入async/await來簡化異步的使用。
先來舉個簡單的例子,假如,你和女朋友逛街。你的目的是去看新上市的華為P40手機,而你女朋友是去看新款的衣服。你們的逛街流程是這樣的。
import time
def clothes_shop():
print("女朋友看衣服..")
time.sleep(8)
print("...出來了")
def huawei_shop():
print("體驗手機..")
time.sleep(5)
print("...出來了")
print(time.ctime(), "開始逛街")
clothes_shop()
huawei_shop()
print(time.ctime(), "結束.")
運行結果:
Thu Apr 16 00:08:22 2020 開始逛街
女朋友看衣服..
...出來了
體驗手機..
...出來了
Thu Apr 16 00:08:35 2020 結束.
假設單位是分鍾,你們總共耗時13分鍾。
接下來,看看用異步是如何處理的:
import asyncio
import time
async def shop(delay, what):
print(what)
await asyncio.sleep(delay)
print("...出來了")
async def main():
task1 = asyncio.create_task(shop(8, '女朋友看衣服..'))
task2 = asyncio.create_task(shop(5, '體驗手機..'))
print(time.ctime(), "開始逛街")
await task1
await task2
print(time.ctime(), "結束.")
asyncio.run(main())
通過 async/await 語法進行聲明,是編寫 asyncio 應用的推薦方式。
- async 聲明一個函數為異步函數。
- await 聲明處理比較耗費時的動作。
- asyncio.run() 函數用來運行最高層級的入口點 main() 函數。
- asyncio.create_task() 函數用來並發運行作為 asyncio 任務 的多個協程。
其實,思路非常簡單,就是你和女朋友各逛各自的,先出來的等等對方。
嚴重警告!提醒廣大直男,現實生活中千萬不要這么思考問題。一定要陪女朋友一起看衣服,還要主動去付錢。
來看看運行結果:
Thu Apr 16 00:19:19 2020 開始逛街
女朋友看衣服..
體驗手機..
...出來了
...出來了
Thu Apr 16 00:19:27 2020 結束.
假設單位是分鍾,只需要8分鍾搞定。
通過上面的例子,可以看到 task1、task2仍然有前后順序,這種前后順序的時間可以忽略不計。但是,我們也是可以使用asyncio.gather()方法並發運行任務。
#……
async def main():
print(time.ctime(), "開始逛街")
await asyncio.gather(
shop(8, '女朋友看衣服..'),
shop(5, '體驗手機..')
)
print(time.ctime(), "結束.")
asyncio.run(main())
運行結果同上,這里就不再貼了。