变量的作用域
从作用域上来说,C语言可以定义4种不同的变量:全局变量,静态全局变量,局部变量,静态局部变量。
下面仅从函数作用域的角度分析一下不同的变量,假设所有变量声明不重名。
- 全局变量(
int gVar;
),在函数外声明。全局变量,所有函数共享,在任何地方出现这个变量名都是指这个变量。 - 静态全局变量(
static sgVar
),其实也是所有函数共享,但是这个会有编译器的限制,算是编译器提供的一种功能。 - 局部变量(函数/块内的
int var;
),不共享,函数的多次执行中涉及的这个变量都是相互独立的,他们只是重名的不同变量而已。 - 局部静态变量(函数中的
static int sVar;
),本函数间共享,函数的每一次执行中涉及的这个变量都是这个同一个变量。
上面几种作用域都是从函数的角度来定义作用域的,可以满足所有我们对单线程编程中变量的共享情况。 现在我们来分析一下多线程的情况。
在多线程中,多个线程共享除函数调用栈之外的其他资源。 因此上面几种作用域从定义来看就变成了。
- 全局变量,所有函数共享,因此所有的线程共享,不同线程中出现的不同变量都是这同一个变量。
- 静态全局变量,所有函数共享,也是所有线程共享。
- 局部变量,此函数的各次执行中涉及的这个变量没有联系,因此,也是各个线程间也是不共享的。
- 静态局部变量,本函数间共享,函数的每次执行涉及的这个变量都是同一个变量,因此,各个线程是共享的。
线程安全资源管理器的由来
在多线程系统中,进程保留着资源所有权的属性,而多个并发执行流是执行在进程中运行的线程。 如 Apache2 中的 worker,主控制进程生成多个子进程,每个子进程中包含固定的线程数,各个线程独立地处理请求。 同样,为了不在请求到来时再生成线程,MinSpareThreads 和 MaxSpareThreads 设置了最少和最多的空闲线程数; 而 MaxClients 设置了所有子进程中的线程总数。如果现有子进程中的线程总数不能满足负载,控制进程将派生新的子进程。
当 PHP 运行在如上类似的多线程服务器时,此时的 PHP 处在多线程的生命周期中。 在一定的时间内,一个进程空间中会存在多个线程,同一进程中的多个线程公用模块初始化后的全局变量, 如果和 PHP 在 CLI 模式下一样运行脚本,则多个线程会试图读写一些存储在进程内存空间的公共资源(如在多个线程公用的模块初始化后的函数外会存在较多的全局变量)。
此时这些线程访问的内存地址空间相同,当一个线程修改时,会影响其它线程,这种共享会提高一些操作的速度, 但是多个线程间就产生了较大的耦合,并且当多个线程并发时,就会产生常见的数据一致性问题或资源竞争等并发常见问题, 比如多次运行结果和单线程运行的结果不一样。如果每个线程中对全局变量、静态变量只有读操作,而无写操作,则这些个全局变量就是线程安全的,只是这种情况不太现实。
为解决线程的并发问题,PHP 引入了 TSRM: 线程安全资源管理器(Thread Safe Resource Manager)。 TRSM 的实现代码在 PHP 源码的 /TSRM 目录下,调用随处可见,通常,我们称之为 TSRM 层。 一般来说,TSRM 层只会在被指明需要的时候才会在编译时启用(比如,Apache2+worker MPM,一个基于线程的MPM), 因为 Win32 下的 Apache 来说,是基于多线程的,所以这个层在 Win32 下总是被开启的。
TSRM的实现
进程保留着资源所有权的属性,线程做并发访问,PHP 中引入的 TSRM 层关注的是对共享资源的访问, 这里的共享资源是线程之间共享的存在于进程的内存空间的全局变量。 当 PHP 在单进程模式下时,一个变量被声明在任何函数之外时,就成为一个全局变量。
首先定义了如下几个非常重要的全局变量(这里的全局变量是多线程共享的)。
10年架构师领你架构-成长之路-(附面试题(含答案))
(腾讯T3-T4)打造互联网PHP架构师教程目录大全,只要你看完,薪资立马提升2倍(持续更新)
原文链接:segmentfault.com
/* The memory manager table */
static tsrm_tls_entry **tsrm_tls_table=NULL;
static int tsrm_tls_table_size;
static ts_rsrc_id id_count;
/* The resource sizes table */
static tsrm_resource_type *resource_types_table=NULL;
static int resource_types_table_size;
**tsrm_tls_table
的全拼 thread safe resource manager thread local storage table,用来存放各个线程的tsrm_tls_entry
链表。tsrm_tls_table_size
用来表示**tsrm_tls_table
的大小。id_count
作为全局变量资源的 id 生成器,是全局唯一且递增的。*resource_types_table
用来存放全局变量对应的资源。resource_types_table_size
表示*resource_types_table
的大小。
其中涉及到两个关键的数据结构 tsrm_tls_entry