PHP的线程安全与非线程安全

关于PHP的线程安全和非线程安全,一直以来只知道IIS下用线程安全,其他情况使用非线程安全即可,但其实这样的理解是错误的,因此通过此文章整理一下PHP线程安全和非线程安全当中所涉及到的知识点,建立完整的知识树。


PHP的线程安全与非线程安全

背景-线程安全(TS)与非线程安全(NTS)

以下为PHP的线程安全与非线程安全版本的区别简要描述

由于Windows使用的是线程的工作模式,使用CGI模式运行PHP的话会非常慢,因此一般在IIS下会将PHP配置成ISAPI方式运行。但是由于常用的PHP扩展都是以多进程的思想来设计的,这些扩展在ISAPI的方式运行时候出错就会搞垮IIS,因此在ISAPI方式下需要使用TS的PHP,而使用CGI方式运行的使用NTS的PHP即可。
为了兼顾IIS下PHP的效率和安全,微软给出了FastCGI的解决方案,并且作为主要方案。

以上是TS版本和NTS版本的区别以及使用场景,由此引入两个问题:

  • 为什么ISAPI多线程下需要线程安全?
  • TS版本PHP安全在哪里?

为什么ISAPI多线程下需要线程安全?

简介

之所以在ISAPI方式中需要使用TS版本的PHP,是因为以多进程思想开发的扩展在ISAPI中如果出错会导致IIS崩溃,那为什么多进程思想开发的扩展运行出错会导致IIS崩溃呢?下面会对此进行说明。

IIS和ISAPI

IIS是Windows平台的一个互联网基本服务(百度-IIS),而ISAPI是扩展的API,扩展IIS功能,将用户请求转发给程序进行处理并且返回(百度-ISAPI)。
ISAPI服务扩展是一个可以被IIS加载和调用的DLL,因此和服务器是共用同一地址空间,简单来说就是属于同一个进程。而CGI模式不一样,CGI是属于单独的一个进程。

进程与线程

进程

程序代码执行的实体、资源拥有者,包含了程序指令、当前状态和相关系统资源。进程拥有资源的所有权,这些资源包括内存、I/O通道、I/O设备和文件等。

线程

程序实际的执行者。参与执行和调度,线程有一个唯一对应的进程。

进程和线程的区别与关系

如图,在单线程进程模型中,进程的表示包括进程控制块和用户地址空间,以及在进程执行中管理调用/返回行为的用户栈和内核栈。而多线程进程模型中,进程仍然只有一个与之关联的进程控制块和用户地址空间,但是每一个线程都有其独立的栈和线程控制块。
进程中所有线程是共享进程的状态和资源,进程创建时候申请的内存地址空间,之后,进程中的线程都驻留在这一块地址空间中,可以互相访问到相同的数据。
线程进程模型

结论

PHP中的静态变量等资源,在多线程方式下是属于共享资源,即不同线程可以对同一变量进行访问和修改。例如线程A初始化了一个变量$a=1,然后线程B可以修改变量$a=2,这样线程A在读取变量$a的时候已经是2了,此情况下可能会导致线程A出错。因此在ISAPI下需要使用TS版本的PHP。

TS版本PHP安全在哪里?

为了解决线程的安全问题,引入了TSRM层,主要是处理共享变量的线程间访问问题。详情见PHP中线程安全

扩展-进程与线程的产生

参考自 Java并发编程:进程和线程之由来
进程是最先被设计出的,由于进程的出现,操作系统可以“同时”执行多个程序,操作系统也是以进程作为执行和调度的单位的。进程之间是互不干扰的,如果进程之间需要互相通信,则需要操作系统的介入,例如linux中的unixsocket。
随着后来程序的复杂化,一个程序中需要包含很多子任务,例如收集数据和显示图表。大部分子任务是可以并行执行的,如果依然以进程作为执行调度单位的话,那么子任务只能一个接一个地执行,即使在多核中也无法实现并行。后来设计出了线程,进程拥有资源,而线程负责执行和调度,如此进程就可以并发地执行子任务了。


参考