python異步
在Python編程的領域中,異步處理就像一位高效的時間管理大師,能讓程序在處理IO密集型任務時大幅提升效率。Python 3.5引入的async和await語法糖,讓原本復雜的異步編程變得清晰易懂。接下來,我們就通過日常生活中的例子和簡單代碼,深入理解協程、async/await及asyncio框架的核心概念。
IO密集型任務:指任務執行過程中大部分時間花費在等待輸入/輸出(I/O)操作完成(如網絡請求、文件讀寫、數據庫查詢等)。在此期間,CPU處于空閑狀態,無需進行大量計算。典型場景包括:
網頁爬蟲(頻繁發起網絡請求并等待響應)
文件批量讀寫(等待磁盤數據傳輸)
數據庫交互(等待查詢結果返回)
CPU密集型任務:指任務執行過程中需要大量CPU計算資源,幾乎不涉及I/O等待(如數據加密/解密、圖像渲染、科學計算等)。此時CPU持續處于高負載狀態,主要用于邏輯運算或數值處理。典型場景包括:
- 視頻編碼/解碼
- 大規模數據統計分析
- 復雜數學模型運算
協程是什么?
想象你來到圖書館,準備度過充實的一天,想一邊查閱資料,一邊完成練習題。
- 進程:就好比整個圖書館,是程序運行的環境,里面有各種資源和空間供任務開展。
- 線程:像是圖書館里不同的自習室,每個自習室可以容納不同的人做不同的事,它們相對獨立又共享圖書館的資源。
- 協程:則是你本人,在等資料打印出來(
IO等待)的時間里,不會干坐著,而是立刻開始做練習題;當練習題遇到不會的,又切換去看看資料是否打印好。哪件事先有結果(資料打印好了或者做出了一道題)就先處理哪件。這就是協程的核心 —— 在單線程內靈活切換任務,避免因等待IO操作而浪費時間。
協程的關鍵特性:
- 在單線程內執行,任務切換成本極低
- 特別適合處理網絡請求、文件讀寫這類
IO密集型任務 - 無法利用多核
CPU,處理CPU密集型任務需要搭配多進程
異步和同步
同步執行(串行):
你先去資料室提交打印申請,然后守在打印機旁,直到資料打印完,才回到座位開始做練習題。整個過程總耗時 = 等資料打印的20分鐘 + 做練習題的30分鐘 = 50分鐘。
異步執行(并行):
你提交打印申請后,不需要一直等著,立刻回到座位開始做練習題。在做題過程中,資料打印好了就去取,取完資料繼續做題。總耗時 = 30分鐘(最長任務時間)。
核心差異:
- 同步:任務按順序依次執行,必須等前一個任務完成才能進行下一個
- 異步:任務啟動后不阻塞后續操作,通過回調或事件通知任務完成
async/await
- 定義異步函數(協程)
import asyncio
# 用async聲明異步函數
async def print_material(duration):
# await 等待異步操作,不阻塞線程
await asyncio.sleep(duration)
print(f'打印材料耗時{duration}秒')
- 執行單個協程
asyncio.run(print_material(2))

- 并發執行多個任務
async def main():
# 創建多個任務
task1 = asyncio.create_task(print_material(2))
task2 = asyncio.create_task(print_material(3))
task3 = asyncio.create_task(print_material(1))
# 等待所有任務完成
await asyncio.gather(task1, task2, task3)
asyncio.run(main())

其中:await只能用于可等待對象(如asyncio.sleep()),直接使用time.sleep()會阻塞線程。asyncio.create_task()用于注冊并發任務,asyncio.gather()可批量等待任務完成
協程和線程

實際案例:
- 下載 1000 張圖片: 協程方案能在單線程內高效完成,線程方案創建大量線程容易導致資源耗盡
- 數據加密計算: 線程配合多進程可以利用多核
CPU,協程無法提升計算速度
asyncio框架
asyncio是Python內置的異步框架,它就像一個智能管家,負責管理協程的執行流程,其內部實現異步的原理主要包含以下幾個關鍵部分:

1. 事件循環(Event Loop)
事件循環是 asyncio 的核心,它就像是一個不停運轉的 “調度員”。這個調度員手中有一張任務清單,不斷地檢查清單里的任務是否有可以執行的。當某個協程遇到await時,就會暫停執行,并把執行權交回事件循環。事件循環會記錄下這個協程暫停的位置,然后去檢查其他任務。一旦await后面的異步操作完成(比如asyncio.sleep()時間結束),事件循環就會把這個協程重新放到任務清單中,等待合適的時機繼續執行。
比如,在前面圖書館的例子中,你在等待資料打印時開始做題,這個 “切換任務” 的動作就像是事件循環在調度。當資料打印好這個事件發生,事件循環就會提醒你去取資料。
import asyncio
async def task1():
print("任務1開始執行")
await asyncio.sleep(2)
print("任務1執行完畢")
async def task2():
print("任務2開始執行")
await asyncio.sleep(1)
print("任務2執行完畢")
async def main():
loop = asyncio.get_event_loop()
task_list = [loop.create_task(task1()), loop.create_task(task2())]
await asyncio.gather(*task_list)
asyncio.run(main())

其中,事件循環loop負責調度task1和task2,根據await asyncio.sleep()的時間,靈活安排任務的執行順序。
- 任務(Task)和Future
asyncio.Task是對協程的進一步封裝,它代表一個具體的異步任務。當我們使用asyncio.create_task()創建任務時,實際上是將協程包裝成了一個Task對象,并加入到事件循環的任務隊列中。Task對象有自己的狀態(如 pending、running、done 等),事件循環通過這些狀態來管理任務的執行。
Future則用于表示異步操作的最終結果。當一個協程完成時,會將結果存儲在對應的Future對象中。比如,當一個網絡請求的協程獲取到數據后,數據就會存放在相關的Future里,其他協程可以通過await這個Future來獲取結果。
- 回調機制
asyncio支持為任務添加回調函數。當一個異步任務完成時,事件循環會自動調用預先設置的回調函數。這就好比你點外賣時,設置了外賣送達時的提醒,外賣送到后手機就會觸發提醒(回調函數執行)。
import asyncio
def callback(future):
print(f"任務結果:{future.result()}")
async def async_task():
await asyncio.sleep(3)
return "任務完成"
async def main():
task = asyncio.create_task(async_task())
task.add_done_callback(callback)
await task
asyncio.run(main())
其中,當async_task執行完畢,callback函數就會被調用,輸出任務的結果。
- 協程的掛起和恢復
await關鍵字是實現協程掛起和恢復的關鍵。當協程執行到await時,會暫停當前協程的執行,并將控制權交回事件循環。此時,事件循環可以去執行其他可運行的協程。當await后面的操作完成,事件循環會恢復該協程的執行,從暫停的位置繼續運行。這種機制使得在單線程內可以高效地處理多個異步任務,避免了因等待IO而造成的線程阻塞。
核心概念:
- 事件循環(Event Loop):異步任務的調度中心,不斷檢查任務狀態
- 任務(Task):對協程的封裝,用于管理執行狀態
- Future:表示異步操作的最終結果,可通過await獲取

浙公網安備 33010602011771號