Python 面试 · 并发与性能
高频问法
- GIL 是什么?对你选并发模型有什么影响?
- 多线程、多进程、asyncio 怎么选?
- CPU 密集任务怎么办?
- IO 密集(调很多 API)怎么办?
- 为什么说「别在异步里写阻塞代码」?
口述参考
1)GIL
CPython 有全局解释器锁,同一进程里同一时刻大致只有一个线程执行 Python 字节码。
所以:纯 Python 计算靠多线程加速有限;等 IO 时线程切换仍能提高吞吐。
2)怎么选
| 场景 | 更合适 |
|---|---|
| 等网络/磁盘 | 多线程或 asyncio |
| 算得凶(图片/加密/大循环) | multiprocessing / 进程池,或换 Java/原生扩展 |
| 大量并发连接 | asyncio 常见 |
| 要共享复杂可变状态 | 小心锁;进程间用队列更清晰 |
3)CPU 密集
- 进程池拆任务
- 关键路径用更合适的语言/服务
- 向量化(NumPy)等减少纯 Python 循环
别回答「开 100 个线程就快了」。
4)IO 密集(调模型 API、爬数据)
concurrent.futures.ThreadPoolExecutor简单够用- 或
asyncio+ httpx/aiohttp - 一定要:超时、限流、重试退避,否则把对方接口打挂或自己卡死
5)异步里的阻塞
async def 里若调用同步 requests.get 或长时间 time.sleep,会堵住事件循环,其它协程也停。
阻塞调用要放进线程池,或换异步库。
和 Java 对比(加分)
- Java:线程是一等公民,业务服务常用线程池。
- Python 脚本:快速 IO 并发很香;长期高并发业务服务我仍倾向 Java 生态。
体现你是选型而不是站队。
追问预备
- 「协程和线程差别?」→ 协程协作式调度、更轻;线程抢占、由系统调度。
- 「死锁?」→ 多把锁顺序不一致;脚本里尽量少锁,用队列传递数据。