Extending and Embedding PHP-扩展和移植PHP(六)

来源:互联网 发布:淘宝h5页面用户 编辑:程序博客网 时间:2024/04/30 08:31

zend 线程安全

在PHP发展初期,只做为单进程CGI方式运行不关心线程安全,因为进程空间不会在请求执行完后还存在,一个内部变量只要被正确的初始化,就能在全局范围内声明,存取和改变,且不计后果。任何没有被清理的资源都会在CGI进程销毁时被释放。

后来,PHP被嵌入到多进程的WEB服务器中,例如APACHE,一个内部变量仍然能够通过动态的请求被全局定义和安全的存取,只要在请求的开始它被正确的初始化,在请求的结束被清理掉。因为每个进程空间在同一时间只有一个请求在处理,这时,关于请求的内存管理被增加进来以避免资源使用失控。


接下来单进程多线程模式的服务器开始出现,需要新的方式处理全局数据。最后出现了一个新层,叫TSRM(thread safe resource management,线程安全资源管理)。


线程安全与非线程安全的声明

在一个简单的无线程应用里,你很可能声明一个全局变量在源文件的头部,编译器在你的数据段里分配内存并返回指针。

在多线程应用里,每个线程都需要自己的数据元,必须给每个线程分配独立的资源。线程在使用数据时能正确的找到内存块的指针。

线程安全数据池

在一个扩展的MINIT阶段,扩展调用一次或多次ts_allocate_id()方法告诉TSRM层有多少数据要被存储。TSRM给数据段增加内存空间,然后返回一个唯一标识符。

typedef struct {
    int sampleint;
    char *samplestring;
} php_sample_globals;
   int sample_globals_id;
  PHP_MINIT_FUNCTION(sample)
{
    ts_allocate_id(&sample_globals_id,
    sizeof(php_sample_globals),
    (ts_allocate_ctor) php_sample_globals_ctor,
    (ts_allocate_dtor) php_sample_globals_dtor);
    return SUCCESS;
}

当在一个请求中需要存取数据时,扩展会从TSRM获取一个指向当前资源池的指针,根据ts_allocate_id()方法返回的资源ID做适当的偏移。换话话说,根据上面的代码,这句SAMPLE_G(sampleint) = 5;就是一个例子,在上面的MINIT中。在线程安全环境里,这句代码通常用宏来执行:

(((php_sample_globals*)(*((void ***)tsrm_ls))[sample_globals_id-1])->sampleint =5;

如果你在读上面代码的时候遇到麻烦不要担心,它已经被很好的集成在了PHPAPI里面,开发人员无需关心它。

不用线程的情况

由于在一个线程安全的环境下存取全局数据会包含在资源池查找正确偏移的开销,和相同情况下非线程环境比较执行较慢,在非线程情况下,数据是确实来自于全局,数据的地址是在编辑时候计算好的。

考虑上面的例子,无线程情况下:

typedef struct {
   int sampleint;
   char *samplestring;
} php_sample_globals;
   php_sample_globals sample_globals;
PHP_MINIT_FUNCTION(sample)
{
   php_sample_globals_ctor(&sample_globals TSRMLS_CC);
   return SUCCESS;
}

第一件要注意的事是声明一个结构而不是一个int型,更容易在进程空间里定义一个结构体。这也意味着这句代码SAMPLE_G(sampleint) = 5;只要改成sample_globals.sampleint = 5;,简单快速有效。

无线程的情况也具有进程隔离的优点,如果一个请求出现问题,不会导致整个服务器崩溃。




原创粉丝点击