全部文章

Python异步编程

一、为什么要学异步编程?

想象你是一位餐厅服务员,现在有两种工作方式:
传统同步方式

  1. 接待顾客A → 去厨房下单 → 等待厨师做菜(干等着不做事)

  2. 菜做好后送给A → 接待顾客B → 重复等待...
    异步方式

  3. 接待顾客A → 去厨房下单 → 立即回来接待顾客B

  4. 当A的菜做好时,厨房会通知你 → 你再去送菜

异步编程的核心就是:让等待的时间不被浪费,特别适合处理大量I/O操作(如网络请求、文件读写)。

通过合理使用 async 和 await,可以编写出高性能的并发程序,尤其适用于需要处理大量I/O操作的场景(如Web服务器、爬虫等)。


二、同步 vs 异步 vs 多线程对比

类型资源消耗适用场景典型问题
同步阻塞 简单任务、低频请求 性能差,无法处理高并发
多线程 中等 I/O 密集型任务 GIL 限制、锁竞争
多进程 CPU 密集型任务 进程切换开销大,进程间通信开销大
异步非阻塞 极低 I/O 密集型高并发任务 代码复杂度稍高


三、异步编程核心概念

1. 事件循环 (Event Loop)

就像餐厅的调度中心,持续监听并处理事件(如网络响应到达、定时任务触发)。

2. 协程 (Coroutine)

可以暂停和恢复的函数,用 async def 定义:

async def fetch_data():  # 这是一个协程
    print("开始获取数据")
    await asyncio.sleep(1)  # 模拟I/O等待
    print("数据获取完成")

async 关键字的作用

定义协程函数
使用 async def 声明的函数称为协程函数。调用该函数时,不会立即执行其内部代码,而是返回一个协程对象

直接调用一个协程函数时,它不会执行函数体内的任何代码,而是直接返回一个 cor程对象(Coroutine Object)。此时,函数内部的代码(包括 await 之前的代码)均未被触发执行。

import asyncio

async def my_async_func():
    print("这是 await 之前的代码")  # 不会立即执行
    await asyncio.sleep(1)        # 更不会执行到这里
    print("这是 await 之后的代码")

# 直接调用协程函数,返回协程对象,但代码未执行
coroutine = my_async_func()
print("协程对象已创建:", coroutine)  # 输出:<coroutine object my_async_func at ...>

此时不会有任何打印输出,因为函数体代码根本未运行。

协程的执行依赖事件循环

要让协程函数内部的代码真正执行,必须通过以下方式之一:

  • 将协程对象提交给事件循环(Event Loop)运行(例如 asyncio.run())。

  • 在其他协程中通过 await 调用它。

正确执行协程的方式

# 方式1:使用 asyncio.run() 运行协程
async def main():
    await my_async_func()

asyncio.run(main())  # 输出:
                     # 这是 await 之前的代码
                     # (等待1秒后)
                     # 这是 await 之后的代码

# 方式2:直接运行协程
asyncio.run(my_async_func())  # 同上

await 关键字的作用

    • 挂起当前协程
      表示"此处需要等待,当协程执行到 await 表达式时,会暂停当前协程,将控制权交还给事件循环。此时,事件循环可以调度执行其他协程。只能在协程内使用

async def fetch_data():
    print("开始获取数据")
    await asyncio.sleep(2)  # 模拟耗时操作(如网络请求)
    print("数据获取完成")
    • 等待异步操作完成
      await 后通常跟随一个可等待对象(如另一个协程、Future 或 Task)。事件循环会监控这些对象的完成状态,当它们完成后,原协程会从暂停处恢复执行。


3.异步执行流程示例

假设有两个协程 task1 和 task2

import asyncio

async def task1():
    print("Task1 开始")
    await asyncio.sleep(1)
    print("Task1 结束")

async def task2():
    print("Task2 开始")
    await asyncio.sleep(2)
    print("Task2 结束")

async def main():
    await asyncio.gather(task1(), task2())  # 并发执行两个协程

asyncio.run(main())

输出

Task1 开始
Task2 开始
(等待1秒)
Task1 结束
(再等待1秒)
Task2 结束

执行过程

  1. task1 执行到 await asyncio.sleep(1) 时暂停,事件循环转去执行 task2

  2. task2 执行到 await asyncio.sleep(2) 时也暂停,事件循环空闲,等待异步操作完成。

  3. 1秒后,task1 的 sleep 完成,恢复执行并打印“Task1 结束”。

  4. 再过1秒后,task2 的 sleep 完成,恢复执行并打印“Task2 结束”。

4.概念总结

阶段行为
协程函数被(非协程)直接调用 返回协程对象,不执行任何代码(包括 await 前后的代码)
协程被事件循环执行 从函数入口开始执行代码,直到遇到第一个 await 时暂停,等待异步操作完成
await 的作用 暂停当前协程,交出控制权给事件循环,等待右侧的异步操作完成后再恢复执行

四、快速入门:3分钟上手异步

基础示例:感受异步的时间魔法

import time
# 非异步执行
def work(workname,second):
    print(f'{workname}:',workname)
    time.sleep(second) # 非阻塞等待
    print(f'{workname}:',"完成")

def main():
    work("烤面包",3)
    work("煮咖啡",2)
    work("煎鸡蛋",1)
main()
''' 非异步,只能串行完成任务,总耗时等于所有任务单独耗时总和:6秒! 烤面包: 开始 烤面包: 完成 煮咖啡: 开始 煮咖啡: 完成 煎鸡蛋: 开始 煎鸡蛋: 完成 '''
import asyncio
# 异步执行 
async def work(workname, second):
    print(f'{workname}:', "开始")
    await asyncio.sleep(second)  # 非阻塞等待
    print(f'{workname}:', "完成")

async def main():
    #     定义任务列表
    works = [work("烤面包", 3), work("煮咖啡", 2), work("煎鸡蛋", 1)]
    #     并行执行所有任务    
    await asyncio.gather(*works)  # “*” 符号在函数调用时用于 解包可迭代对象(如列表、元组等),等价于 gather(work("烤面包",3),work("煮咖啡",2),work("煎鸡蛋",1))
asyncio.run(main())

'''
三个任务同时开始:总耗时 ≈3秒(同步执行需要6秒!)

烤面包: 开始
煮咖啡: 开始
煎鸡蛋: 开始
煎鸡蛋: 完成
煮咖啡: 完成
烤面包: 完成
'''

关键区别:同步 vs 异步

行为同步代码(阻塞)异步代码(非阻塞)
等待I/O操作 整个线程被阻塞,无法执行其他任务 当前协程暂停,事件循环执行其他任务
资源占用 高(每个任务占用独立线程/进程) 低(单线程内切换协程)
适用场景 CPU密集型任务 I/O密集型任务(如网络请求、文件操作)

五、实战进阶:异步网络请求

使用 aiohttp 实现高并发爬虫

import asyncio
from datetime import datetime
import aiohttp
from bs4 import BeautifulSoup

headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"}

async def fetch(session, url):
    try:
        async with session.get(url, headers=headers) as response:
            return await response.text()
    except Exception as e:
        print(f"请求{url}出错了", e)
        return None

async def main():
    urls = [
               "https://www.baidu.com",
               "https://www.zhihu.com",
               "https://www.jd.com"
           ] * 10  # 重复10次模拟30个请求
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
    #     统计成功数量
    sucess = sum(1 for res in results if res)
    print(f"请求总数{len(urls)},成功总数:{sucess}")

# 总共执行时间 ≈1秒(同步需要30秒+)
bgtime = datetime.now()
asyncio.run(main())
endtime = datetime.now()
print("总耗时", endtime - bgtime)  # 总耗时 0:00:01.393797

1. 如何控制并发量?

使用信号量 (Semaphore):

import asyncio
from datetime import datetime
import aiohttp
from bs4 import BeautifulSoup

headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"}

async def fetch(session, url, semaphore):
    try:
        async with semaphore:
            async with session.get(url, headers=headers) as response:
                print(f"{url}开始执行")
                await asyncio.sleep(2)
                print(f"{url}执行完成")
                return await response.text()
    except Exception as e:
        print(f"请求{url}出错了", e)
        return None

async def main():
    urls = [
               "https://www.baidu.com",
               "https://www.zhihu.com",
               "https://www.jd.com"
           ] * 10  # 重复10次模拟30个请求
    async with aiohttp.ClientSession() as session:
        # 创建信号量
        semaphore = asyncio.Semaphore(3)  # 最大并发数3
        # 创建任务列表(每个任务携带信号量:semaphore)
        tasks = [fetch(session, url, semaphore) for url in urls]
        # 并发执行所有任务(但最多同时3个)
        results = await asyncio.gather(*tasks)
    #     统计成功数量
    sucess = sum(1 for res in results if res)
    print(f"请求总数{len(urls)},成功总数:{sucess}")


asyncio.run(main())
'''
https://www.jd.com   开始执行
https://www.baidu.com开始执行
https://www.zhihu.com开始执行

https://www.jd.com   执行完成
https://www.baidu.com执行完成
https://www.zhihu.com执行完成

https://www.baidu.com开始执行
https://www.jd.com   开始执行
https://www.zhihu.com开始执行

https://www.baidu.com执行完成
https://www.jd.com   执行完成
https://www.baidu.com开始执行    #这里后面可能是打印错乱,但是可以得出结论,同时一定只有三个任务在执行
https://www.zhihu.com执行完成
'''

2. 如何处理超时?

 aiohttp的timeout

aiohttp的timeout是针对单个请求的连接和读取超时

asyncio的wait_for超时

asyncio的wait_for是对整个协程的超时控制

最佳实践:多层超时控制

为全面防御各类超时问题,推荐组合使用以下策略

import aiohttp
import asyncio
from aiohttp import ClientTimeout

async def fetch(session, url):
    try:
        # 1. 网络层超时:限制连接、传输的底层时间
        async with session.get(url, timeout=ClientTimeout(
            total=5,          # 总超时##ClientTimeout(total=5) 表示整个请求(包含连接、发送、接收)的总时间不得超过 5 秒:从发起请求到完整接收响应体的最大耗时
            connect=2,        # 连接建立超时
            sock_read=3       # 单次数据读取超时
        )) as response:
            
            # 2. 业务逻辑层超时:限制响应处理时间
            html = await asyncio.wait_for(
                process_response(response), 
                timeout=5
            )
            return html
            
    except asyncio.TimeoutError:
        print(f"请求超时: {url}")
        return None

async def process_response(response):
    # 模拟耗时操作(如解析大文件)
    await asyncio.sleep(3)
    return await response.text()

各超时参数的典型值参考

场景connectsock_connectsock_readtotal
内网高速服务 1s 1s 2s 5s
公网常规 API 3s 3s 5s 10s
高延迟网络 5s 5s 10s 20s

总结

  • ClientTimeout(total=5) 是 网络协议层的安全保障,防止底层操作无限挂起。

  • 应结合 asyncio.wait_for 实现 业务逻辑层的超时控制(如响应解析、数据处理)。

  • 合理配置超时参数可显著提升程序的 健壮性 和 用户体验

补充:

ClientSession 的 会话级超时 和 session.get() 的 请求级超时 

在 aiohttp 中,ClientSession 的 会话级超时 和 session.get() 的 请求级超时 是两个不同层级的超时控制。它们的核心区别在于 作用范围和优先级。以下是详细解释:


1. 作用范围对比

超时设置位置作用范围适用场景
ClientSession 会话级超时 会话内所有请求的 默认超时 统一管理大部分请求的超时规则(全局默认值)
session.get() 请求级超时 仅针对当前请求的 独立超时 针对特殊请求单独调整超时(覆盖会话级默认值)

2. 优先级规则

  • 请求级超时 > 会话级超时
    如果同时在会话和请求中设置超时,请求级配置会覆盖会话级配置。

示例代码:
from aiohttp import ClientTimeout, ClientSession

async with ClientSession(
    timeout=ClientTimeout(total=5)  # 会话级超时:5秒
) as session:
    
    # 请求1:使用会话级超时(total=5)
    await session.get("https://api.example.com/data")
    
    # 请求2:使用请求级超时(total=3,覆盖会话级设置)
    await session.get(
        "https://api.example.com/slow",
        timeout=ClientTimeout(total=3)  # 优先级更高
    )

3. 超时参数详解

ClientTimeout 对象支持多种细粒度超时配置:

timeout = ClientTimeout(
    total=10,        # 总超时(从请求开始到响应结束)
    connect=3,       # 建立TCP连接的超时(含DNS解析)
    sock_connect=2,  # 单次TCP连接尝试的超时(DNS解析完成后)
    sock_read=5      # 从Socket读取数据的单次等待超时
)
超时触发场景
  • connect=3:若DNS解析或TCP握手超过3秒,触发超时。

  • sock_read=5:若服务器在发送响应头或响应体时,连续5秒未发送数据,触发超时。

  • total=10:若整个请求(从发起到完成)超过10秒,触发超时(优先级最高)。


4. 最佳实践

场景1:统一超时策略

所有请求使用相同的超时规则:

async with ClientSession(
    timeout=ClientTimeout(total=5)
) as session:
    # 所有请求默认使用 total=5
    await session.get("https://api.example.com/1")
    await session.get("https://api.example.com/2")
场景2:差异化超时策略

大部分请求用默认超时,特殊请求单独调整:

async with ClientSession(
    timeout=ClientTimeout(total=5)
) as session:
    # 默认使用 total=5
    await session.get("https://api.example.com/fast")
    
    # 高风险接口设置更短超时
    await session.get(
        "https://api.example.com/unstable",
        timeout=ClientTimeout(total=2)
    )
    
    # 大文件下载设置更长超时
    await session.get(
        "https://api.example.com/large-file",
        timeout=ClientTimeout(total=30)
    )

5. 底层实现逻辑

  1. 会话级超时
    初始化 ClientSession 时,将超时配置绑定到会话对象,作为后续所有请求的默认值。

  2. 请求级超时
    发起请求时,若指定了 timeout 参数,会创建一个新的 ClientTimeout 对象,覆盖会话级配置。


总结

  • 会话级超时:统一管理默认规则,提高代码复用性。

  • 请求级超时:灵活覆盖默认值,适应不同请求需求。

  • 组合使用:既能保持代码简洁,又能精准控制关键请求。

3. 同步代码调用异步函数

方法1:直接运行事件循环

import asyncio

async def async_task():
    print("异步任务开始")
    await asyncio.sleep(1)
    print("异步任务完成")
    return "结果"

# 同步函数中调用异步任务
def sync_function():
    print("同步代码开始")
    result = asyncio.run(async_task())  # 启动事件循环
    print("获取异步结果:", result)
    print("同步代码结束")

sync_function()

输出

同步代码开始
异步任务开始
(1秒后)
异步任务完成
获取异步结果: 结果
同步代码结束

方法2:在已有事件循环中运行

import asyncio

async def async_task():
    print("异步任务开始")
    await asyncio.sleep(1)
    return "结果"

def sync_function():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    try:
        result = loop.run_until_complete(async_task())
        print("获取异步结果:", result)
    finally:
        loop.close()

sync_function()

注意

  • 在 Jupyter Notebook 或已有事件循环的环境(如 FastAPI)中,直接使用 await

    # 在 Jupyter Cell 中直接运行
    await async_task()  # 无需 asyncio.run()

七、最佳实践

    1. 避免阻塞操作
      不要在协程中使用 time.sleep(),改用 await asyncio.sleep()

    2. 优先使用异步库

      同步库异步替代
      requests aiohttp
      sqlite3 aiosqlite
    3. 监控性能
      使用 uvloop 提升性能:

import uvloop
uvloop.install()  # 放在文件开头

八、总结与下一步

你已经掌握的:

  • ✅ 异步编程的核心概念

  • ✅ 协程的创建与使用

  • ✅ 高效处理网络请求

  • ✅ 常见问题解决方案

推荐学习路线:

  1. 深入学习 asyncio 官方文档

  2. 掌握 aiohttp 的高级用法

  3. 了解异步数据库操作(如 asyncpg

  4. 探索 Web 框架(如 FastAPI


动手实践是掌握异步编程的最佳方式! 尝试用异步改写你现有的爬虫或API调用代码,感受性能的飞跃吧!

补充

await的使用

在Python异步编程中,正确使用await关键字至关重要。以下是判断何时需要添加await的分步指南:


1. 识别异步函数

函数定义:任何用async def定义的函数为协程函数,调用时必须加await

async def fetch_data():
    return "Data"

async def main():
    result = await fetch_data()  # 必须加await
    print(result)
  • 第三方库函数:若库的文档标明某函数是异步的(如aiohttp.ClientSession.get()),调用时需加await


2. 理解异步操作场景

以下操作通常需要await

  • 网络请求(如aiohttphttpx

  • 文件I/O(如aiofiles

  • 数据库操作(如异步ORM库tortoise-orm

  • 睡眠/延时asyncio.sleep()

  • 其他协程调用(包括标准库的异步API)

示例:

import aiohttp
import asyncio

async def fetch_url(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:  # 需要await
            return await response.text()  # 需要await

async def main():
    html = await fetch_url("https://example.com")  # 需要await
    print(html[:100])

asyncio.run(main())

3. 避免常见错误

错误1:忘记加await

async def task():
    print("Task done")

async def main():
    task()          # ❌ 错误:未加await,task()不会执行
    await task()    # ✅ 正确

asyncio.run(main())

错误提示
RuntimeWarning: coroutine 'task' was never awaited


错误2:在同步函数中使用await

def sync_func():
    await async_func()  # ❌ 语法错误:不能在同步函数中使用await

4. 检查代码上下文

  • await只能用于异步函数内部
async def async_func():
    await some_async_op()  # ✅ 正确

def sync_func():
    await some_async_op()  # ❌ 语法错误

事件循环必须存在:异步代码需通过asyncio.run()或事件循环启动:

# 正确写法
async def main():
    await async_func()

if __name__ == "__main__":
    asyncio.run(main())

5. 调试技巧

  • 使用IDE提示:现代IDE(如PyCharm、VS Code)会标记未正确使用await的位置。

  • 阅读错误信息

    • SyntaxError: 'await' outside async function → 在同步函数中误用await

    • RuntimeWarning: coroutine 'xxx' was never awaited → 忘记加await

  • 逐步注释法:若不确定某处是否需要await,可注释掉await观察程序行为变化。


总结表格

场景是否需要await示例
调用async def函数 await fetch_data()
调用同步函数 requests.get(url)
调用异步I/O操作 await response.text()
使用asyncio.sleep() await asyncio.sleep(1)
在同步函数中使用await 否(语法错误) def sync(): await ... ❌

最终原则

  • 黄金法则:见到async def函数或异步库函数,调用时必加await

  • 文档优先:第三方库的异步API需查阅文档确认用法。

  • 保持简洁:将异步逻辑集中在async函数中,避免与同步代码混用。

为什么这样设计?

异步编程的核心是非阻塞协作式多任务。协程的延迟执行特性使得:

  1. 资源高效:可以创建大量协程对象而无需立即分配执行资源。

  2. 灵活调度:事件循环能自由决定何时执行哪个协程。

  3. 逻辑清晰:通过 await 显式声明异步操作的等待点。

asyncio.Lock异步锁

在异步编程中,尽管协程在单线程内交替执行,但如果多个协程需要修改同一个全局变量,仍然可能因操作被中断(通过await)导致竞态条件。为了保证数据一致性,asyncio提供了协程专用的锁机制(如asyncio.Lock)。以下是详细示例和解释:

示例场景

多个协程同时对一个全局计数器 counter 进行递增操作,每次递增需要模拟一个耗时的I/O操作(用await asyncio.sleep(0.1))。

1. 无锁情况下的竞态条件

import asyncio

counter = 0  # 全局变量

async def increment():
    global counter
    temp = counter
    await asyncio.sleep(0.1)  # 模拟I/O等待,协程在此处切换
    counter = temp + 1  # 问题点:其他协程可能已经修改了counter

async def main():
    tasks = [asyncio.create_task(increment()) for _ in range(100)]
    await asyncio.gather(*tasks)
    print(f"Final counter value: {counter}")

asyncio.run(main())

输出

Final counter value: 1  # 预期是100,但因竞态条件导致结果错误

原因
协程在 await asyncio.sleep(0.1) 处释放控制权,其他协程可能在此期间读取到相同的 temp 值,导致最终结果远小于预期。


2. 使用 asyncio.Lock 解决竞态条件

import asyncio

counter = 0
lock = asyncio.Lock()  # 协程专用锁

async def increment():
    global counter
    async with lock:  # 自动加锁和解锁
        temp = counter
        await asyncio.sleep(0.1)  # 协程切换时锁仍被持有
        counter = temp + 1

async def main():
    tasks = [asyncio.create_task(increment()) for _ in range(100)]
    await asyncio.gather(*tasks)
    print(f"Final counter value: {counter}")

asyncio.run(main())

输出

Final counter value: 100  # 结果正确

关键机制

  • async with lock 确保代码块在执行期间不会被其他协程中断。

  • 即使协程在 await 处切换,锁仍被当前协程持有,其他协程需等待锁释放后才能进入临界区。


锁的工作原理

  1. 加锁:协程通过 async with lock 进入临界区时,锁被占用。

  2. 等待:其他协程尝试获取锁时会被挂起,直到锁被释放。

  3. 解锁:协程退出 async with 代码块时自动释放锁。


其他同步原语

除了 Lockasyncio 还提供以下工具:

同步原语用途
asyncio.Semaphore 限制同时访问资源的协程数量(如控制最大并发连接数)
asyncio.Event 协程间的事件通知(如等待某个条件达成后唤醒所有等待协程)
asyncio.Condition 复杂条件同步(类似 threading.Condition

最佳实践

  1. 最小化锁的范围:只在操作共享资源时加锁,尽快释放。

  2. 避免嵌套锁:防止死锁(如协程A持有锁1,协程B持有锁2,互相等待对方释放)。

  3. 优先使用不可变数据:减少共享状态的需求(如使用函数式编程风格)。


总结

  • 异步编程仍需同步:协程交替执行可能导致非原子操作的竞态条件。

  • 使用 asyncio.Lock:保护共享资源的原子性操作。

  • 选择合适工具:根据场景选择 SemaphoreEvent 或 Condition

通过合理使用同步机制,可以在保持异步高效并发的同时,确保数据的一致性。

异步锁 vs 多线程锁

特性异步锁 (asyncio.Lock)多线程锁 (threading.Lock)
底层实现 用户态实现,基于事件循环调度 内核态实现,依赖操作系统线程调度
切换成本 纳秒级(无系统调用) 微秒级(需陷入内核)
阻塞行为 非阻塞,协程让出事件循环 阻塞线程,导致线程挂起
适用场景 单线程内协程同步 多线程同步
GIL 影响 无关(单线程无 GIL 竞争) 受 GIL 限制

何时需要异步锁?

场景是否需要锁示例
协程只读共享数据 读取全局配置
协程修改独立资源 每个协程操作自己的数据库连接
协程修改共享资源且操作包含 await 全局计数器、共享文件写入
global counter
temp = counter
await asyncio.sleep(0.1) # 模拟I/O等待,协程在此处切换
counter = temp + 1 # 问题点:其他协程可能已经修改了counter
协程修改共享资源但操作是原子的 counter += 1(无 await 中断)

六、最佳实践

    1. 尽量减少共享状态
      使用不可变数据、线程隔离设计(如为每个协程分配独立资源)。

    2. 优先使用队列通信
      通过 asyncio.Queue 实现协程间数据传递,避免直接共享变量。

import asyncio

async def producer(queue):
    for i in range(10):
        await queue.put(i)
        await asyncio.sleep(0.1)

async def consumer(queue):
    while True:
        item = await queue.get()
        print(f"消费: {item}")
        queue.task_done()

async def main():
    queue = asyncio.Queue()
    tasks = [
        asyncio.create_task(producer(queue)),
        asyncio.create_task(consumer(queue))
    ]
    await asyncio.gather(*tasks)

asyncio.run(main())
  1. 锁的范围最小化
    仅保护必要代码块,尽快释放锁以提升并发度。


总结

    • 异步编程绕过了 GIL:这是其性能优势的核心来源。

    • 协程间仍可能竞争共享资源:需通过 asyncio.Lock 等机制同步。

    • 锁的使用成本极低:异步锁的切换开销可忽略不计,不会成为性能瓶颈。

posted @ 2025-03-27 22:24  指尖下的世界  阅读(86)  评论(0)    收藏  举报