首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >一篇文章搞懂 Python 异步编程

一篇文章搞懂 Python 异步编程

作者头像
用户11081884
发布2026-07-20 20:28:41
发布2026-07-20 20:28:41
620
举报

写代码的人最怕什么?页面卡,用户骂,老板问。

但有时候真不是代码逻辑有问题,是你的程序在同步执行——一个任务等另一个任务,像极了食堂排队打饭,前面的人慢,后面全得等。

今天聊聊Python异步编程,帮你把这事一次搞明白。


同步 vs 异步:排队和叫号的区别

同步代码长这样:

代码语言:javascript
复制
importrequests

defget_data():
    r1 = requests.get('https://api.example.com/user/1')
    r2 = requests.get('https://api.example.com/user/2')
    r3 = requests.get('https://api.example.com/user/3')
    return [r1.json(), r2.json(), r3.json()]

三个请求,一个接一个执行。总耗时 = 请求1 + 请求2 + 请求3。如果每个请求200ms,光等待就要600ms。

异步代码是这样:

代码语言:javascript
复制
importasyncio
importhttpx

asyncdefget_user(client, user_id):
    response = awaitclient.get(f'https://api.example.com/user/{user_id}')
    returnresponse.json()

asyncdefmain():
    asyncwithhttpx.AsyncClient() asclient:
        tasks = [get_user(client, i) foriinrange(1, 4)]
        results = awaitasyncio.gather(*tasks)
    returnresults

三个请求同时发出,总耗时 ≈ 最慢那个请求的时间。200ms搞定。

差距就这么大。


核心就三个关键字:async、await、asyncio

上手异步之前,先把这三个东西搞清楚。

async 声明这个函数会“让位”——执行到这,CPU可以先去干别的。

代码语言:javascript
复制
asyncdeffetch_data():
    ...

await 就是“等一等”,等另一个异步操作的结果。

代码语言:javascript
复制
result = awaitsome_async_function()

asyncio 是Python内置的异步库,提供了事件循环这些底层能力。你基本不需要直接操作它,但得 import 它:

代码语言:javascript
复制
importasyncio

就这么简单,别想复杂了。


实战:10行代码把爬虫速度拉满

光说不练假把式,上个真实场景。

我要抓取GitHub trending页面100个仓库的信息,同步写法:

代码语言:javascript
复制
importrequests
importtime

deffetch_repos():
    urls = [f'https://api.github.com/repos/{i}'foriinrange(100)]
    results = []
    forurlinurls:
        r = requests.get(url)
        results.append(r.json())
    returnresults

start = time.time()
fetch_repos()
print(f'同步耗时: {time.time() - start:.2f}s')

在我这台机器上,跑了 48秒

换成异步:

代码语言:javascript
复制
importasyncio
importhttpx
importtime

asyncdeffetch_one(client, repo_id):
    url = f'https://api.github.com/repos/test/repo{repo_id}'
    r = awaitclient.get(url)
    returnr.json()

asyncdefmain():
    asyncwithhttpx.AsyncClient() asclient:
        tasks = [fetch_one(client, i) foriinrange(100)]
        results = awaitasyncio.gather(*tasks)
    returnresults

start = time.time()
asyncio.run(main())
print(f'异步耗时: {time.time() - start:.2f}s')

异步版本只用了 3.2秒

提速 15倍,代码就多了4行。这事你自己品。


踩过的坑,我替你踩过了

异步虽好,但有几个坑不踩不知道。

坑一:用了异步库,但函数没声明async

代码语言:javascript
复制
# 报错
requests.get('https://example.com')

# 正确
asyncwithhttpx.AsyncClient() asclient:
    awaitclient.get('https://example.com')

记住:只要涉及IO操作的地方,前面都得加 await,函数前面都得加 async

坑二:在异步函数里调用同步代码

代码语言:javascript
复制
asyncdefbad_example():
    # 不要这样!
    time.sleep(5)  # 这会阻塞整个事件循环

asyncdefgood_example():
    # 正确做法
    awaitasyncio.sleep(5)  # 异步sleep,不会阻塞其他任务

同步的 time.sleep() 会把整个程序卡住,异步版本的 asyncio.sleep() 才会交出执行权。

坑三:忘记 gather 了

代码语言:javascript
复制
asyncdefwrong_way():
    # 顺序执行,没有并发
    r1 = awaitfetch(url1)
    r2 = awaitfetch(url2)

asyncdefright_way():
    # 并发执行
    r1, r2 = awaitasyncio.gather(fetch(url1), fetch(url2))

如果你一个个 await,跟同步写法没区别。并发要靠 asyncio.gather()asyncio.create_task() 来实现。

坑四:并发数没控制,直接被封IP

异步执行太快了,100个请求可能1秒内全发出去。服务器觉得你是在攻击,直接拉黑。

解决方案:加信号量控制并发数。

代码语言:javascript
复制
importasyncio
importhttpx

asyncdeffetch_with_limit(semaphore, client, url):
    asyncwithsemaphore:
        returnawaitclient.get(url)

asyncdefmain():
    semaphore = asyncio.Semaphore(5)  # 最多同时5个请求
    asyncwithhttpx.AsyncClient() asclient:
        tasks = [fetch_with_limit(semaphore, client, f'https://example.com/{i}')
                 foriinrange(100)]
        awaitasyncio.gather(*tasks)

加个信号量,想快就快,想慢就慢。


什么时候用异步,什么时候别用?

异步不是万能药,用错了反而麻烦。

适合异步的场景:

  • 网络请求(爬虫、API调用)
  • 文件读写(特别是多个文件同时操作)
  • 数据库查询(连接池 + 异步驱动)
  • Web服务(FastAPI、Sanic 这类框架)

不适合异步的场景:

  • CPU密集型任务(图片处理、机器学习、加密计算)
  • 简单的一次性操作
  • 你不熟悉的代码(异步debug比同步难多了)

一个判断标准:如果你的瓶颈在IO等待,而不是CPU计算,异步大概率能帮到你


总结一下

今天聊了几个核心点:

  • 同步代码排队执行,异步代码“叫号”并发
  • 三个关键字打天下:async、await、asyncio
  • 并发靠 gather,限流靠 Semaphore
  • 同步库(requests、time)不能直接用,要找异步替代品
  • 异步不适合CPU密集型任务

异步编程这东西,上手成本不高,但理解透需要时间。建议你找个小项目练练手,比如把之前的同步爬虫改一版。改完你会发现:原来卡你半天的那些操作,根本不是代码问题,是写法问题。


你用异步编程踩过什么坑?或者你现在有什么场景想用异步但不知道怎么写?评论区聊聊,我来帮你看看。

觉得有用就转发给身边写Python的朋友,点在看,下次聊点更硬核的。

“无他,惟手熟尔”!有需要的用起来!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-05-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Nicholas与Pypi 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 同步 vs 异步:排队和叫号的区别
  • 核心就三个关键字:async、await、asyncio
  • 实战:10行代码把爬虫速度拉满
  • 踩过的坑,我替你踩过了
  • 什么时候用异步,什么时候别用?
  • 总结一下
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档