Python 3.12 的 per-interpreter GIL 仅作用于子解释器,对 threading.Thread 无效;线程仍共享主解释器 GIL,无法真正并行;子解释器需通过 channel 或共享内存通信,且 API 尚属实验性。

Python 3.12 的 per-interpreter GIL 不能让你改几行 threading.Thread 就跑满多核——它只对子解释器生效,而子解释器和线程是两套互不兼容的并发模型。
per-interpreter GIL 不等于 threading.Thread 的 GIL 解锁
你写 threading.Thread 启动十个线程,它们全挤在主解释器里,争抢同一把 GIL,CPU 密集任务依然卡在单核。这不是 bug,是设计如此。per-interpreter GIL 是给 interpreters.create() 创建的子解释器用的,每个子解释器自带独立 GIL 和内存空间。
-
threading模块完全感知不到子解释器的存在,也不能跨解释器传函数、类或闭包 - 主线程调用
interpreters.create()后,必须用字符串形式传代码(如interp.run("x = 42; print(x)")) - 子解释器之间默认零共享:不能直接读写彼此的
globals()、模块变量或sys.path
数据交换只能靠 channel 或共享内存
子解释器要协作,得显式建立通信通道。Python 3.12 提供 _interpreters.channel_create() 和配套的 channel_send()/channel_recv(),但只收发 bytes,所有 Python 对象都得手动序列化。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 传小数据用
json.dumps().encode()+json.loads()最稳妥 - 传 numpy 数组或大字典时,
pickle可能失败(子解释器无共享模块状态),建议改用memoryview+shared_memory模块 - 别在循环里频繁调用
channel_send():每次调用都有内核态切换开销,实测 1MB 数据每秒最多传 200 次左右
子解释器启动快,但 API 还不稳定
标准库的 interpreters 模块在 3.12 中是实验性功能(import interpreters 可用),但底层仍依赖私有 C API _interpreters 和 _xxsubinterpreters。这些模块名带下划线,随时可能变。
立即学习“Python免费学习笔记(深入)”;
- 不要在生产环境用
_interpreters.create()直接创建——它绕过标准库封装,出错时 traceback 不友好 - 用
interpreters.create()启动后,必须显式调用interp.close(),否则资源泄漏(尤其 fd 和内存) - 子解释器崩溃不会终止主进程,但会留下僵尸解释器状态,需用
interpreters.list_all()定期检查
真正能压满多核的场景,是那种“输入独立、计算隔离、输出汇总”的任务,比如批量解析不同用户的 JSON 请求、并行哈希文件、分片处理日志流。一旦逻辑里出现共享计数器、全局缓存或锁竞争,子解释器反而比 multiprocessing 更难调试——因为错误不抛异常,而是静默失败或 channel 阻塞超时。

















