python 有没有类似 go 的并发编程

接入与开发

给 Python 开发者对照 Go 的并发模型,讲清可用替代方案、选型判断和落地时的超时并发细节。

你之前写 Go,goroutine 加 channel 顺手就上了;换到 Python 想照抄这套,发现 GIL、asyncio、多进程各说各话,不知道该选哪个。这篇不重讲语法,给一条从任务类型出发的对照思路,配上落地时要定的超时、并发上限和排查顺序,让你能按手里的活儿直接挑方案。

你之前写 Go,goroutine 加 channel 顺手就上了;换到 Python 想照抄这套,发现 GIL、asyncio、多进程各说各话,不知道该选哪个。先给个结论:Python 没有 goroutine 的等价物,但每一层都有替代品,关键是按手里的任务类型挑,而不是找一个万能机制。

这篇不重讲语法,给你一条从任务类型出发的对照思路,配上落地时要定的超时、并发上限和排查顺序。看完你应该能直接判断手里的活儿该用线程、进程还是协程,而不是先写一遍再返工。

先对齐心智模型:Go 和 Python 差在哪

Go 的 goroutine 是运行时调度的轻量线程,channel 是语言级原语,调度器帮你把几万个并发单元摊到少量 OS 线程上。Python 没有这个统一的运行时层,你得自己在三套机制里挑:threading、multiprocessing、asyncio。它们各自解决不同层面的问题,不是互相替代的关系。

GIL
全局解释器锁,同一时刻只允许一个线程执行 Python 字节码,纯计算任务拿不到并行
让出时机
线程在 IO 等待时释放 GIL,所以网络请求、文件读写这类场景多线程仍然有效
组合方式
调度靠 asyncio 事件循环,通信靠 queue 或 Channel 类库,隔离靠多进程,三层各管一段

按任务类型选替代方案:一张对照思路

下面这张表按任务特征给方向,不是硬性规定。你的实际场景可能跨两行,那就先按主要瓶颈选主机制,再补一层辅助。

任务特征推荐机制要留意的代价
大量网络请求、文件读写asyncio 配异步客户端同步库混进来会卡死事件循环
少量 CPU 密集计算multiprocessing 或进程池进程启动和数据序列化开销
混合负载进程池跑协程,或队列分流架构复杂度上升,调试更难
想随手起几千个并发单元先确认是 IO 等待型再用协程内存和调度成本会先顶上来
  • 异步客户端优先选原生支持 asyncio 的库,别用同步驱动硬套线程池包装
  • 进程池的任务参数和返回值必须可序列化,传不了的对象要提前拆成基础类型
  • 混合负载里,CPU 活丢给进程、IO 活留在事件循环,别让一个进程既算又等
  • 并发数不是越高越好,先测出下游能接住多少,再定上限

落地时最容易踩的坑

大部分「Python 并发不给力」的抱怨,最后都指向同一个原因:异步函数里混进了同步阻塞调用。这会让整个事件循环卡住,表现是并发数上不去、延迟一起涨,看起来像性能问题,其实是写法问题。

  1. 排查事件循环阻塞在协程里临时加日志,看有没有某段代码执行时间明显偏长;同步数据库驱动和 requests 是最常见的元凶,换成异步版本或丢到线程池跑
  2. 确认并发瓶颈在哪并发一提就报限流错误,说明瓶颈在下游接口而不是你的代码;去 /guides/429-concurrency-backoff 看排队与退避的分档做法,别硬提并发
  3. 分开设超时连接超时、读取超时、整体超时要分开定,参考 /guides/api-timeout-tuning 的分档清单,否则排查时分不清是连不上还是算得慢

还有一个和 Go 差异最大的地方:多进程之间共享状态要靠队列或外部存储,直接改全局变量不会生效。如果你习惯 Go 的共享内存模型,这里会栽跟头。

上线前要跑一遍的检查清单

选完方案不算完,上线前这几步能帮你少走一轮返工。重点是先有基线数字,再看变化,别拿没测过的感觉当依据。

  • 先量基线:用 /tools/speed-test 测出单请求延迟,再压并发,没有基线就不知道优化有没有效果
  • 压测时同时观察并发数、错误率、P99 延迟,只看平均值会把长尾问题藏起来
  • 限流和重试要配对设计:什么错误重试、重试几次、退避怎么涨,别让重试把下游打得更死
  • 日志里记下任务类型、并发档位、失败原因,下次调参才有参照

这套清单对 IO 密集型任务最有效。如果你的任务里 CPU 计算占大头,重点应该放在进程数和数据切分上,而不是压并发连接数。

什么时候这套对比不适用

不是所有场景都值得纠结并发模型。以下几种情况,换机制带来的收益可能还不如把同步逻辑写清楚。

  • 瓶颈在模型接口的响应速度和额度限制,不在语言层:先去 /sites 和 /rankings 看服务本身的稳定性,再谈并发优化
  • 延迟敏感、单请求耗时很短的场景:多进程启动开销可能比省下的时间还多,先用同步写法
  • 团队没人维护过异步代码:硬上 asyncio 的长期维护成本可能高于直接用线程池,选型要把人力算进去

判断标准很简单:如果换方案后,你没法用一句话说清「它到底省掉了哪一段等待或计算」,那这个改动大概率不值得做。价格和稳定性这类服务侧数据,去 /price-watch 核对当时的数字,别拿记忆里的值做决策。

下一步把你手里最慢的那个任务拿出来,先按任务类型对一遍上面的对照表,再跑一次基线压测;服务侧的价格和稳定性数据到 /price-watch 和 /sites 核对当时的数字。

常见问题

Python 里有没有跟 goroutine 完全对等的东西?

没有。goroutine 是语言运行时层面的轻量并发单元,Python 侧最接近的是 asyncio 协程,但它是单线程事件循环,CPU 密集任务并不能因此并行。要真并行只能上多进程,或者说换用别的运行时。

IO 密集任务用 asyncio 还是多线程?

两者都能让 IO 等待期间去干别的活。区别在数量级和写法:几百个并发连接多线程够用,几千级连接 asyncio 更省资源,但要求整条链路都是异步库,混进一个同步调用就会拖慢整个循环。

CPU 密集任务怎么处理才不掉进 GIL 的坑?

用多进程而非多线程,把计算拆成独立任务提交给进程池。要注意进程间传数据要序列化,大对象传输开销不小;如果单个任务本身就很短,进程开销可能盖过收益,这时考虑换 C 扩展或外部计算服务。

本文依据各平台公开文档与官方公告整理,只做中文归纳;具体做法、参数与限制以官方文档为准。