banner
kanes

kanes

小白也能懂的Python協程與asyncio指南

小白也能懂的 Python 協程與 asyncio 指南#

一、從生活場景理解異步編程#

1.1 買奶茶的兩種方式#

假設你要買三杯奶茶,每杯製作需要 2 分鐘:

傳統方式(同步):

def 買奶茶_同步():
    for _ in range(3):
        等待2分鐘()  # 幹等著不動
        拿奶茶()

# 總耗時:3×2=6分鐘 ❌

聰明方式(異步):

async def 買奶茶_異步():
    訂單列表 = [下單(), 下單(), 下單()]  # 同時下單
    await asyncio.gather(*訂單列表)  # 邊等邊玩手機

# 總耗時:2分鐘 ✅

1.2 協程就像外賣小哥#

想像一個外賣小哥同時處理多個訂單:

  • 到 A 店取餐(等待時去 B 店)
  • 途中接新訂單(靈活調整路線)
  • 送達後立即接下一單(不浪費時間)

這就是協程的工作方式!不需要多個小哥(線程),一個就能高效完成任務。


二、最簡協程入門(附可運行代碼)#

2.1 Hello 協程版#

import asyncio

async def 打招呼(name):  # 關鍵1:async定義協程
    print(f"{name}開始做事")
    await asyncio.sleep(1)  # 關鍵2:遇到等待就掛起
    print(f"{name}事情做完啦")

async def 主任務():
    await asyncio.gather(
        打招呼("小明"),
        打招呼("小紅")
    )

asyncio.run(主任務())  # 關鍵3:啟動事件循環

輸出結果:

小明開始做事
小紅開始做事
(等待1秒)
小明事情做完啦
小紅事情做完啦

2.2 執行過程圖解#

|700x615


三、必須掌握的 3 個核心概念#

3.1 協程三要素#

要素說明類比
async def聲明協程函數給外賣訂單貼上 "加急" 標籤
await暫停並讓出控制權小哥暫時離開去送其他訂單
事件循環協調所有任務的調度員外賣平台派單系統

3.2 常見誤區清單#

  1. 錯誤:在普通函數中使用 await

    def 普通函數():
        await asyncio.sleep(1)  # 報錯!
    
  2. 錯誤:忘記創建任務

    async def 錯誤示例():
        # 順序執行,沒有並發!
        await 任務1()
        await 任務2()
    
  3. 正確做法:

    async def 正確示例():
        task1 = asyncio.create_task(任務1())
        task2 = asyncio.create_task(任務2())
        await task1
        await task2
    

四、手把手實戰:下載多張圖片#

4.1 同步版本(龜速)#

import requests

def 下載圖片(url):
    print(f"開始下載 {url}")
    data = requests.get(url).content
    with open("圖片.jpg", "wb") as f:
        f.write(data)
    print(f"下載完成 {url}")

def 主函數():
    urls = ["url1", "url2", "url3"]  # 假設3個圖片地址
    for url in urls:
        下載圖片(url)

# 總耗時:單張耗時 × 數量

4.2 異步版本(飛一般的感覺)#

import aiohttp

async def 異步下載(url):
    async with aiohttp.ClientSession() as session:
        print(f"開始下載 {url}")
        async with session.get(url) as response:
            data = await response.read()
        with open(f"{url.split('/')[-1]}", "wb") as f:
            f.write(data)
        print(f"下載完成 {url}")

async def 主任務():
    urls = ["url1", "url2", "url3"]
    await asyncio.gather(*[異步下載(url) for url in urls])

asyncio.run(主任務())

4.3 性能對比#

圖片數量同步耗時異步耗時速度提升
1020s2s10 倍
100200s5s40 倍

五、常見問題解答#

Q1:協程和多線程有什麼區別?#

特性協程多線程
資源占用一個線程搞定所有每個線程需要獨立資源
切換方式主動讓出控制權被系統強制切換
適用場景適合大量 IO 操作適合計算密集型任務
編程難度需要理解異步語法需要處理線程安全問題

Q2:什麼時候不能用協程?#

  • 需要大量 CPU 計算的場景(如視頻轉碼)
  • 使用不支持異步的庫(比如傳統的數據庫驅動)
  • 需要跨核並行計算(需結合多進程)

Q3:如何調試協程程序?#

  1. 使用asyncio.run()作為入口

  2. 在協程內使用普通 print 語句

  3. 使用專業調試器:

    import logging
    logging.basicConfig(level=logging.DEBUG)
    

六、最佳學習路線建議#

6.1 新手三步走#

  1. 先寫同步代碼理解業務流程
  2. 將耗時操作替換為 async/await
  3. asyncio.gather實現並發

6.2 推薦練習項目#

項目類型實現功能技能點
天氣查詢器同時查詢多個城市天氣基礎異步請求
網頁監控定時檢查多個網站狀態異步定時任務
聊天機器人同時處理多個用戶消息並發消息處理

七、記住這 5 句話就夠了#

  1. async def:聲明協程的起跑線
  2. await:遇到 IO 就舉手暫停
  3. 事件循環:幕後總調度員
  4. create_task:把協程變成可執行任務
  5. asyncio.run():程序啟動開關

此文由 Mix Space 同步更新至 xLog
原始鏈接為 https://blog.kanes.top/posts/default/asyncio


載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。