摘要:中专门为解决线程安全的问题抽象出了一个线程安全资源管理器,实现原理比较简单既然共用资源这么困难那么就干脆不共用,各线程不再共享同一份全局变量,而是各复制一份,使用数据时各线程各取自己的副本,互不干扰。
1.线程安全资源管理器
PHP的SAPI多数是单线程环境,比如cli、fpm、cgi,每个进程只启动一个主线程,这种模式下是不存在线程安全问题的,但是也有多线程的环境,比如Apache,这种情况下就需要考虑线程安全的问题了,因为PHP中有很多全局变量,比如最常见的:EG、CG,如果多个线程共享同一个变量将会冲突,所以PHP为多线程的应用模型提供了一个安全机制:Zend线程安全(Zend Thread Safe, ZTS)。
PHP中专门为解决线程安全的问题抽象出了一个线程安全资源管理器(Thread Safe Resource Mananger, TSRM),实现原理比较简单:既然共用资源这么困难那么就干脆不共用,各线程不再共享同一份全局变量,而是各复制一份,使用数据时各线程各取自己的副本,互不干扰。
typedef struct { size_t size; //资源的大小 ts_allocate_ctor ctor; //初始化函数 ts_allocate_dtor dtor; int done; } tsrm_resource_type; struct _tsrm_tls_entry { void **storage; //资源数组 int count; //拥有的资源数:storage数组大小 THREAD_T thread_id; //所属线程id tsrm_tls_entry *next; };
如果一个资源会被多线程使用,那么首先需要预先向TSRM注册资源,然后TSRM为这个资源分配一个唯一的编号,并把这种资源的大小、初始化函数等保存到一个tsrm_resource_type结构中,各线程只能通过TSRM分配的那个编号访问这个资源;然后当线程拿着这个编号获取资源时TSRM如果发现是第一次请求,则会根据注册时的资源大小分配一块内存,然后调用初始化函数进行初始化,并把这块资源保存下来供这个线程后续使用。
每个线程拥有一个tsrm_tls_entry结构,当前线程的所有资源保存在storage数组中,下标就是各资源的id。另外所有线程的tsrm_tls_entry结构通过一个数组保存:tsrm_tls_table,这是个全局变量,每个线程的tsrm_tls_entry结构在这个数组中的位置是根据线程id与预设置的线程数(tsrm_tls_table_size)取模得到的,也就是说有可能多个线程保存在tsrm_tls_table同一位置,所以tsrm_tls_entry是个链表,查找资源时首先根据:线程id % tsrm_tls_table_size得到一个tsrm_tls_entry,然后开始遍历链表比较thread_id确定是否是当前线程的。线程本地存储(Thread Local Storage, TLS),在创建完当前线程的tsrm_tls_entry后会把这个值保存到当前线程的TLS中,这样在ts_resource()获取资源时中就可以通过tsrm_tls_get()直接取到了,节省加锁检索的时间。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/28582.html
摘要:但在多线程模式下会有多个,也就是说每个线程都有一个独立的内存池内存分配分配超过内存的申请,与通用的内存申请没有太大差别,只是将申请的内存块通过单链表进行了管理。的分配实际就是分配多个,的分配也是内存分配的基础,它是向系统申请内存的唯一粒度。 1.Zend内存池 内存池是内核中最底层的内存操作,定义了三种粒度的内存块:chunk、page、slot,每个chunk的大小为2M,page大...
摘要:插入一个元素时先将元素按先后顺序插入数组,位置是,再根据的哈希值映射到散列表中的某个位置,将存入这个位置查找时先在散列表中映射到,得到在数组的位置,再从数组中取出元素。目前只有两种类型会使用这种机制。 1.变量结构 typedef struct _zval_struct zval; typedef union _zend_value { zend_long ...
摘要:局部变量中局部变量分配在结构上,每次执行都会生成一个新的,局部变量在执行之初分配,然后在执行结束时释放,这是局部变量的生命周期。 1.局部变量 PHP中局部变量分配在zend_execute_data结构上,每次执行zend_op_array都会生成一个新的zend_execute_data,局部变量在执行之初分配,然后在执行结束时释放,这是局部变量的生命周期。 读写操作:局部变量通过...
摘要:代码的编译的解析过程任务就是将代码转化为数组,代码里的所有信息都保存在数组中,然后将数组交给引擎执行,就是内核具体执行的命令,比如赋值加减操作函数调用等,每一条都对应一个处理,这些是提前定义好的函数。 1.PHP代码的编译 PHP的解析过程任务就是将PHP代码转化为opcode数组,代码里的所有信息都保存在opcode数组中,然后将opcode数组交给zend引擎执行,opcode就是...
阅读 1158·2021-11-23 10:10
阅读 1421·2021-09-30 09:47
阅读 872·2021-09-27 14:02
阅读 2949·2019-08-30 15:45
阅读 3001·2019-08-30 14:11
阅读 3557·2019-08-29 14:05
阅读 1774·2019-08-29 13:51
阅读 2131·2019-08-29 11:33