我們在使用 FastAPI 時常常會看到路徑操作函式會定義為 async def 的函式,而不是一般直接使用 def 聲明,使用 async def 聲明是表示說這個函式是非同步的函式。
今天要講的是並發和非同步的概念,這也是 FastAPI 可以達到高效能的原因之一。
這個章節我們介紹並發的概念,但講到並發就一定會提到並行,所以在這邊我們會一起介紹。
我們使用 FastAPI 官方文件的例子作為講解,該例子為兩個人到漢堡店去買漢堡時的過程。
並發指的是可以在重疊的時間段內執行多個任務的能力,我們將一件大任務拆成多個子任務來執行,當所有子任務都完成後,該項大任務才算完成。
以漢堡店的例子來說,大任務是拿到餐點在位子上享用,子任務就會有:
等等任務,而當我們排隊時,可能沒有做任何事情,單純的在等待,但因為隊伍消化得很快所以沒關係。接著你點完餐找到位子了,坐著等待餐點,但你在等待餐點的時候處於一個閒置的狀態,所以這個時候就可以執行一些高效的任務,像是和你的夥伴聊天、滑手機等等。終於餐點好了,你接獲通知去拿取餐點,回到位子上和你的夥伴一起享用。
以上範例就可以帶出並發的主要概念,當你正在閒置等待狀態的時候,你就可以去執行其他子任務,而當任務執行完你又可以來確認等待的任務完成與否,來進行下一步任務。
並行性指的是多個任務或一個任務的多個部分同時執行,以漢堡店的例子來說,這家店的櫃台有很多個,當你在其中一個櫃台點完餐後,你在櫃台前面等待,而接待你的人跑去廚房做漢堡,這時候若有其他人要點餐只能到其他櫃台去做同樣的流程,而等到漢堡完成後你在拿著你的漢堡去找位子並享用。
以上範例來說並行性的主要概念就是單一執行緒或單一台機器上執行一個任務,有多個任務時就是分配到其他執行緒去執行。
總之,並發不等於並行,但是可以透過並行來做到並發,
非同步的程式碼指的是該程式語言有一種方法可以跟機器說在執行到某個時機時,機器將不得不等待其他任務完成,才能繼續執行下去,而在這段時間,機器可以去做其他的任務。
以剛剛的漢堡店例子就是,當點完餐後,我們要等待餐點做好,而在等待的這段期間我們就可以去找位子,聊天等等。
而在實務上我們等待的事情通常是指說與 CPU 和記憶體的速度相比起來相對較慢的I/O操作,例如:
之所以稱為非同步的原因是,我們不用甚麼都不做來等待這些任務完成,獲取任務結果後再繼續任務。而是可以去執行其他任務再回來檢查剛剛等待的任務完成了沒有。
async def get_db_data():
return data
我們可能會有一個需要較長I/O操作時間的函式,這個時候我們就可以將其宣告為 async 的函式。
@app.get("/")
async def root():
return await get_db_data()
我們在 FastAPI 中便可以在非同步的路徑操作函式中直接使用 await 來執行 get_db_data
這個非同步的函式。
await 這個語法糖就是在告訴機器說,這個操作會花一點時間,你可以先去做其他事情。這邊要注意一件是,await 只能在 async 的函式中使用。
記得在遇到需要花時間的I/O操作時要用非同步的方法,這樣可以大大的提升你的API效能,而在使用時也不要忘記,如果要做的事情沒有支援非同步的話,我們就一樣使用同步的函式就好,FastAPI還是會幫我們優化效能。
現代程式設計語言中的併發性:簡介 |技術憤怒 (deepu.tech)
[Python] async def & await 重點整理 - iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天 (ithome.com.tw)
Concurrency and async / await - FastAPI (tiangolo.com)