本文最初写于2017年,2026年重新整理。早期文章关注TSRM的具体实现,现在更值得保留的是它要解决的问题。

PHP应用最常见的运行方式是FPM:多个worker进程分别处理请求。进程拥有独立地址空间,因此一个worker修改自身的全局状态,不会直接改到另一个worker。

但PHP也可以被构建在多线程环境中。线程共享同一进程的地址空间,如果执行器、编译器或扩展把可变状态放在普通全局变量里,不同线程就可能互相覆盖数据。ZTS(Zend Thread Safety)就是为这种运行模型准备的。

ZTS解决什么

它的基本思路并不神秘:不能安全共享的全局状态,不再让所有线程访问同一份,而是为不同线程提供各自的存储,再通过线程上下文取得当前线程对应的数据。

早期PHP内核通过TSRM(Thread Safe Resource Manager)管理这些资源。具体结构和访问宏随着PHP版本发生过变化,但目标没有变:让原本依赖进程级全局状态的代码,可以在多线程SAPI或嵌入场景中隔离运行。

ZTS不是给业务代码自动加锁,也不能让任意扩展天然线程安全。扩展如果使用自己的全局资源、调用非线程安全的第三方库,仍然需要单独处理并发问题。

什么时候需要

是否启用ZTS取决于PHP运行环境,而不是应用是否“并发”。FPM通过多进程提供并发,通常不需要因为请求量增加就改成ZTS构建。

通常需要考虑ZTS的场景包括:

  • PHP运行在多线程SAPI中;
  • PHP被嵌入一个多线程宿主程序;
  • 使用明确要求ZTS的线程或并行扩展。

ZTS是编译期选项,不能在已经安装好的PHP上临时打开。选择之前还需要确认扩展兼容性和运行方式,而不是把它当作通用性能开关。

进程隔离与线程隔离

FPM的进程模型占用更多独立内存,但隔离边界直观,一个worker崩溃通常不会直接破坏其他worker的地址空间。线程模型共享资源更多,切换成本和内存结构不同,同时也要求运行时与扩展更严格地处理共享状态。

理解ZTS的意义,不在于记住某个版本的内核结构,而在于先判断程序运行在什么并发模型里,再决定状态应当如何隔离。

参考