前面寫的非同步編程的幾篇文章:

  • 非同步編程 101: 是什麼、小試Python asyncio
  • 非同步編程 101:Python async await發展簡史
  • 非同步編程 101:寫一個事件循環

廢話不多說,上代碼:

這個代碼只適用於Python3.7,因為 asyncio.run()在 Python3.7才提出

import asyncio

async def heavy_task():
await asyncio.sleep(2)

async def main():
for _ in range(100):
await heavy_task()

if __name__ == __main__:
asyncio.run(main())

請問上面這個代碼需要多久完成?答案是20秒,而不是我們期望的那樣在2秒內完成。

理解原因需要先搞明白 await到底幹了什麼:事件循環執行到 await的時候,會把當前的協程掛起(暫停),然後看看當前的事件循環池裡面有哪些其他可以執行的協程,接著繼續執行其他的協程。

main()中的 for 循環是一個整體, awaitheavy_task()會把整個 main() 掛起,等到await的 heavy_task()執行完了(也就是兩秒後),接著再返回 main(),從上次落下的地方繼續。而上次落下的地方,還是在這個 for 循環裡面的。

所以,這個程序是非同步的嗎?可以肯定的說是的,針對整個程序是非同步的。但是對於 main(),它的 for循環還是阻塞的。

上述問題的根源在於:我們沒能及時往事件循環裡面添加協程for 循環要等到 heavy_task()結束了再創建下一個協程。要解決這個可以用 asyncio.gather()

前面一篇文章裡面我們的那個代碼:先把所有的協程事先創建好,然後一次性交給 asyncio.gather()

import time
import asyncio
import aiohttp

async def fetch_async(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
status_code = resp.status
print(status_code)

async def visit_async():
start = time.time()
tasks = []
for _ in range(100):
tasks.append(fetch_async(URL))
await asyncio.gather(*tasks)
end = time.time()
print("visit_async tasks %.2f seconds" % (end - start))

if __name__ == __main__:
loop = asyncio.get_event_loop()
loop.run_until_complete(visit_async())

如果你像我一樣真正熱愛計算機科學,喜歡研究底層邏輯,歡迎關注我的微信公眾號:

推薦閱讀:

相關文章