一、异步编程的核心本质(先搞懂「为什么」)

1. 同步 vs 异步:核心差异
  • 同步编程:代码按顺序执行,遇到耗时 IO(网络请求、文件读写、数据库操作)时,线程会阻塞等待结果,CPU 闲置。例:10 个网络请求串行执行,每个耗时 2 秒,总耗时≈20 秒。
  • 异步编程:代码执行到耗时 IO 时,会让出 CPU 控制权,去执行其他任务;等 IO 有结果后,再回调处理,CPU 全程不闲置。例:10 个网络请求异步执行,总耗时≈2 秒(仅等待最长的那个请求)。
2. 异步编程的核心目标

解决IO 密集型任务的效率问题(如爬虫、API 服务、数据库批量操作),而非 CPU 密集型任务(纯计算用多进程更合适)。

二、Python 异步的核心组件(从基础到核心)

1. 基础语法:async/await(Python 3.5+)
关键字 / 函数 作用 用法示例
async def 定义协程函数(Coroutine Function),调用后返回协程对象,不立即执行 ```python
async def async_task():
await asyncio.sleep(2)
return "完成"

|

| `await` | 暂停协程执行,等待「可等待对象」完成,期间让出CPU;**仅能在协程函数内使用** | 见上方示例,等待`asyncio.sleep(2)`(模拟耗时IO) |
| `asyncio.run()` | 启动事件循环,执行指定协程(程序入口常用) | ```python
result = asyncio.run(async_task())
print(result)  # 输出:完成
``` |

#### 2. 核心驱动:事件循环(Event Loop)
- **本质**:异步编程的「总指挥」,负责调度所有协程、管理IO事件、处理回调。
- **核心工作流程**:
  ```mermaid
  graph TD
  A[启动事件循环] --> B[将协程包装为Task加入队列]
  B --> C{是否有可执行任务?}
  C -- 是 --> D[执行任务直到遇到await]
  D --> E[让出CPU,处理下一个任务]
  C -- 否 --> F[等待IO事件触发]
  F --> G[IO完成,回调对应的任务继续执行]
  G --> C
  • 手动操控事件循环(进阶)

    python

    运行

    import asyncio
    
    async def task1():
        print("任务1开始")
        await asyncio.sleep(1)
        print("任务1结束")
    
    async def task2():
        print("任务2开始")
        await asyncio.sleep(2)
        print("任务2结束")
    
    # 手动创建事件循环(适合复杂场景)
    loop = asyncio.get_event_loop()
    # 同时执行多个协程
    tasks = [task1(), task2()]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
    
3. 任务管理:TaskFuture
  • Task:协程的「可执行包装器」,加入事件循环后立即调度(并行执行)。核心用法:asyncio.create_task(coro)(Python 3.7+)

    python

    运行

    async def main():
        # 创建2个Task,立即并行执行
        t1 = asyncio.create_task(task1())
        t2 = asyncio.create_task(task2())
        # 等待所有Task完成
        await t1
        await t2
    
    asyncio.run(main())
    # 输出:
    # 任务1开始
    # 任务2开始
    # 任务1结束
    # 任务2结束
    
  • Future:表示「未来完成的结果」,Task 是 Future 的子类,一般无需手动创建,底层使用。

三、实战场景:异步编程的典型应用

1. 异步网络请求(最常用场景)

需安装异步请求库:pip install aiohttp

python

运行

import aiohttp
import asyncio

# 异步请求单个URL
async def fetch_url(session, url):
    async with session.get(url) as response:
        return await response.text()  # 等待响应结果

# 批量异步请求
async def batch_fetch(urls):
    # 创建异步会话(复用连接,提升效率)
    async with aiohttp.ClientSession() as session:
        # 创建所有请求任务
        tasks = [asyncio.create_task(fetch_url(session, url)) for url in urls]
        # 等待所有任务完成,返回结果列表
        results = await asyncio.gather(*tasks)
        return results

# 测试
if __name__ == "__main__":
    urls = [
        "https://httpbin.org/get?name=1",
        "https://httpbin.org/get?name=2",
        "https://httpbin.org/get?name=3"
    ]
    # 执行异步批量请求
    results = asyncio.run(batch_fetch(urls))
    print(f"共请求{len(results)}个URL,第一个结果:{results[0][:50]}")
2. 异步文件读写(Python 3.7+)

python

运行

import asyncio

async def async_read_file(file_path):
    # 异步打开文件(需使用aiofiles库,Python内置open是同步的)
    # 安装:pip install aiofiles
    import aiofiles
    async with aiofiles.open(file_path, 'r', encoding='utf-8') as f:
        content = await f.read()
        return content

async def main():
    content = await async_read_file("test.txt")
    print(f"文件内容:{content}")

asyncio.run(main())
3. 异步 Web 服务(FastAPI)

FastAPI 是天生支持异步的 Web 框架,性能远超 Flask:

python

运行

from fastapi import FastAPI
import asyncio

app = FastAPI()

# 异步接口
@app.get("/async-task")
async def async_task():
    await asyncio.sleep(2)  # 模拟耗时操作
    return {"message": "异步任务完成"}

# 启动服务:uvicorn main:app --reload
# 访问http://127.0.0.1:8000/async-task,服务可同时处理多个请求(无阻塞)

四、异步编程的常见坑与避坑指南

  1. 混用同步阻塞代码:异步函数内调用time.sleep()requests.get()等同步阻塞函数,会导致整个事件循环卡住!✅ 解决方案:用异步替代(asyncio.sleep()替代time.sleep()aiohttp替代requests)。

  2. await使用错误

    • 错误:在同步函数内用await
    • 错误:直接调用协程函数(如task1())而不awaitcreate_task;✅ 解决方案:await仅在async def函数内使用,协程必须通过asyncio.run()/create_task()执行。
  3. 异步锁(解决并发竞争):多任务修改同一变量时,需用asyncio.Lock()保证原子性:

    python

    运行

    async def update_num(lock, num):
        async with lock:  # 加锁
            num += 1
            await asyncio.sleep(0.1)
            return num
    
    async def main():
        lock = asyncio.Lock()
        num = 0
        tasks = [asyncio.create_task(update_num(lock, num)) for _ in range(10)]
        results = await asyncio.gather(*tasks)
        print(f"最终结果:{results[-1]}")  # 输出:10(无锁可能小于10)
    
    asyncio.run(main())
    

五、异步 vs 多线程 / 多进程:选型建议

编程范式 适用场景 优势 劣势
异步编程 IO 密集型任务(网络、文件、数据库) 轻量级(无线程切换开销)、效率最高 学习曲线稍陡、依赖异步库
多线程 IO 密集型任务(简单场景) 语法简单、兼容同步代码 GIL 限制(CPU 密集型无优势)、线程切换开销
多进程 CPU 密集型任务(计算、数据处理) 利用多核 CPU、无 GIL 限制 内存占用高、进程通信复杂

总结

  1. Python 异步编程的核心是async/await语法 + 事件循环,核心价值是提升IO 密集型任务的执行效率;
  2. 核心组件:协程函数(async def)、Task(并行调度)、事件循环(调度核心);
  3. 典型应用:异步网络请求、异步文件读写、异步 Web 服务(FastAPI);
  4. 避坑关键:避免混用同步阻塞代码,正确使用await,并发竞争用asyncio.Lock()
  5. 选型原则:IO 密集用异步,CPU 密集用多进程,简单 IO 场景可考虑多线程。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐