python 非同步 I/O

蟲師發表於2020-04-22

如果你想了解非同步程式設計,那麼必然會涉及出許多相關概念。

  • 堵塞/非堵塞
  • 同步/非同步
  • 多程式/多執行緒/協程

為什麼我要學習這個話,因為我想搞懂非同步框架和非同步介面的呼叫。所以,我的學習路線是這樣的:

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())

執行結果同上,這裡就不再貼了。

參考:https://docs.python.org/zh-cn/3/library/asyncio.html

相關文章