【PHP内核学习】线程安全

75 篇文章 0 订阅
5 篇文章 1 订阅

PHP同一进程下的多个线程会试图读写一些存储在进程内存空间的公共资源,此时这些线程访问的内存地址空间相同,当一个线程修改时,会影响其它线程,这种共享会提高一些

操作的速度, 但是多个线程间就产生了较大的耦合,并且当多个线程并发时,就会产生常见的数据一致性问题或资源竞争等并发常见问题。


如果每个线程中对全局变量、静态变量只有读操作,而无写操作,则这些个全局变量就是 线程安全的.

为解决线程的并发问题,PHP引入了 TSRM: 线程安全资源管理器(Thread Safe Resource Manager)。 TRSM 的实现代码在 PHP 源码的 /TSRM 目录下,我们称之为 TSRM 层。

我的php源码位置(gentoo  x86_64 GNU/Linux):



PHP的线程安全策略是将线程的共享资源复制多份,进程中的每个线程各自都有一份共享资源的拷贝,各做各的,完全隔离

原理图:


(图片出处:https://i-blog.csdnimg.cn/blog_migrate/c60f9deb725a8db238084139e999a3f0.png


_tsrm_tls_entry(TSRM )结构体:

struct _tsrm_tls_entry {
    void **storage;
    int count;
    THREAD_T thread_id;
    tsrm_tls_entry *next;
};

每个tsrm_tls_entry结构负责表示一个线程的所有全局变量资源,其中thread_id存储线程ID,count记录全局变量数,next指向下一个节点。storage可以看做指针数组,其中每个元素是一个指向本节点代表线程的一个全局变量。最终各个线程的tsrm_tls_entry被组成一个链表结构,并将链表头指针赋值给一个全局静态变量tsrm_tls_table。注意,因为tsrm_tls_table是一个货真价实的全局变量,所以所有线程会共享这个变量,这就实现了线程间的内存管理一致性。


(图片出处:http://blog.codinglabs.org/articles/zend-thread-safety.html


通过一个ts_allocate_id()函数为新的线程分配一个线程安全资源id(thread safe resource id):

/* allocates a new thread-safe-resource id */
TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor)
{
    int i;

    TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new resource id, %d bytes", size));

    tsrm_mutex_lock(tsmm_mutex);//互斥锁

    /* obtain a resource id */
    *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++); //id_count即为当前进程下的线程数量
    TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id));

    /* store the new resource type in the resource sizes table */
    if (resource_types_table_size < id_count) {
        resource_types_table = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count);//重新分配内存
        if (!resource_types_table) {
            tsrm_mutex_unlock(tsmm_mutex);
            TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource"));
            *rsrc_id = 0;
            return 0;
        }
        resource_types_table_size = id_count;
    }
    resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size;
    resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor;
    resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor;
    resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0;

    /* enlarge the arrays for the already active threads */
    for (i=0; i<tsrm_tls_table_size; i++) {
        tsrm_tls_entry *p = tsrm_tls_table[i];

        while (p) {
            if (p->count < id_count) {
                int j;

                p->storage = (void *) realloc(p->storage, sizeof(void *)*id_count);
                for (j=p->count; j<id_count; j++) {
                    p->storage[j] = (void *) malloc(resource_types_table[j].size);
                    if (resource_types_table[j].ctor) {
                        resource_types_table[j].ctor(p->storage[j], &p->storage);
                    }
                }
                p->count = id_count;
            }
            p = p->next;
        }
    }
    tsrm_mutex_unlock(tsmm_mutex);

    TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id));
    return *rsrc_id;
}


---------------------

珠玉在前:

1.http://blog.codinglabs.org/articles/zend-thread-safety.html

2.http://blog.csdn.net/hackooo/article/details/8856225

3.http://www.laruence.com/2008/08/03/201.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值