DISCUZX2.0 核心文件 class_core.php 分析之 – discuz_process

本文介绍DISCUZ中进程锁管理的实现方式,通过加锁确保计划任务不会被同时执行,采用PHP实现类似Linux定时任务的功能,并提供了解锁及状态检查的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:http://summerbluet.com/417


/**
 * 进程锁管理, DISCUZ 的进程管理, 进程管理, 在 DX 的计划任务里面使用!
 * PHP 本身是触发型语言,需要触发才能执行某个操作,而不像 linux 那样可以到指定时间执行指定的计划任务。
 * 所以上面提到的计划任务,实际上都是需要我们触发才能执行的。
 * 
 * 执行的过程采用了加锁的方式,防止在任务被同时执行!
 * 
 * 例子: 
 *      在实现数据调用模块的自动更新的时候调用了函数 block_updatecache (位于 function_block.php), 有这样的判断
 *      如果 discuz_process::islocked('block_update_cache', 5) 为 true 的话 直接 return false; 中断脚本执行!
 *      如果 discuz_process::islocked('block_update_cache', 5) 为 false 的话函数才允许继续执行, 并且在函数
 *      的最后面调用 discuz_process::unlock('block_update_cache');  对进程的解锁!
 * 
 */
class discuz_process
{
    /**
     * 检查进程是否已经上锁, 上锁的话, 当然这个进程就不能继续执行了
     * @param <string>  $process 进程的名称
     * @param <num> $ttl 锁的过期时间, 以秒为单位 !  如果进程没有被锁的话, 系统会自动把
     *                  这个进程加入到进程锁里面, 过期的参数会是 'expiry' => time() + $ttl , 
     *                  默认为 0 , 就是不把此进程进行加锁操作, 不进行时间限制, 必须调用 unlock 才能继续下面的进程!
     * @return <bool> true 此动作已经被加锁, false 则未加锁
     */
    function islocked($process, $ttl = 0) {
        $ttl = $ttl < 1 ? 600 : intval($ttl);
        if(discuz_process::_status('get', $process)) {
            return true;
        } else {
            return discuz_process::_find($process, $ttl);
        }
    }


    /**
     * 删除某个进程或过期进程
     * @param <string> $name 进程名
     */
    function unlock($process) {
        discuz_process::_status('rm', $process);
        discuz_process::_cmd('rm', $process);
    }


    /**
     * 用来优化的, 当一次 DX 请求里面需要执行某个进程多次, 设置一个 "static $plist" 
     * 是一个不错的选择! 不用每一次的去检查内存缓存或者数据库! 需要注意的是在每
     * 一个进程执行完之后需要执行下 unlock 操作, 否则会导致同一个请求的剩余进程无法继续! 
     * 
     * @param <string>  $action  为命令名称, 可以为 set, get, rm, clear
     * @param <string>  $process 进程的名称
     */
    function _status($action, $process) {
        static $plist = array();
        switch ($action) {
            case 'set' : $plist[$process] = true; break;
            case 'get' : return !empty($plist[$process]); break;
            case 'rm' : $plist[$process] = null; break;
            case 'clear' : $plist = array(); break;
        }
        return true;
    }


    /**
     * 检查进程是否存在, 内部函数
     * @param <string>  $process 进程的名称
     * @param <num> $ttl 锁的过期时间, 以秒为单位 !  如果进程没有被锁的话, 系统会自动把
     *                  这个进程加入到进程锁里面, 过期的参数会是 'expiry' => time() + $ttl , 
     *                  默认为 0 , 就是不把此进程进行加锁操作, 不进行时间限制, 必须调用 unlock 才能继续下面的进程!
     * @return <bool> true 此动作已经被加锁, false 则未加锁
     */
    function _find($name, $ttl) {


        if(!discuz_process::_cmd('get', $name)) {   /** 如果不存在获取过期了, 返回的为 FALSE */
            discuz_process::_cmd('set', $name, $ttl);   // 既然不存在就加上去呗
            $ret = false;   
        } else {
            $ret = true;
        }
        discuz_process::_status('set', $name);  // 设置 static $plist[$process] = true;
        return $ret;
    }


    /**
     *  执行命令, 内部函数, _find 的帮助函数
     * @param <string>  $cmd  为命令名称, 可以为 set, get, rm 
     * @param <string>  $process 进程的名称
     * @param <num> $ttl 锁的过期时间, 以秒为单位 !  如果进程没有被锁的话, 系统会自动把
     *                  这个进程加入到进程锁里面, 过期的参数会是 'expiry' => time() + $ttl , 
     *                  默认为 0 , 就是不把此进程进行加锁操作, 不进行时间限制, 必须调用 unlock 才能继续下面的进程!
     * @return <bool> true 此动作已经被加锁, false 则未加锁
     */
    function _cmd($cmd, $name, $ttl = 0) {
        static $allowmem;
        if($allowmem === null) {
            $allowmem = memory('check') == 'memcache';
        }
        if($allowmem) {
            return discuz_process::_process_cmd_memory($cmd, $name, $ttl);
        } else {
            return discuz_process::_process_cmd_db($cmd, $name, $ttl);
        }
    }


    /**
     * 从内存里面读进程信息
     * @param <string>  $cmd  为命令名称, 可以为 set, get, rm 
     * @param <string>  $process 进程的名称
     * @param <num> $ttl 锁的过期时间, 以秒为单位 ! 
     */
    function _process_cmd_memory($cmd, $name, $ttl = 0) {
        return memory($cmd, 'process_lock_'.$name, time(), $ttl);
    }


    /**
     * 从数据库里面读的进程信息
     * @param <string>  $cmd  为命令名称, 可以为 set, get, rm 
     * @param <string>  $process 进程的名称
     * @param <num> $ttl 锁的过期时间, 以秒为单位 ! 
     */
    function _process_cmd_db($cmd, $name, $ttl = 0) {
        $ret = '';
        switch ($cmd) {
            case 'set':
                $ret = DB::insert('common_process', array('processid' => $name, 'expiry' => time() + $ttl), false, true);
                break;
            case 'get':
                $ret = DB::fetch_first("SELECT * FROM ".DB::table('common_process')." WHERE processid='$name'");
                if(empty($ret) || $ret['expiry'] < time()) {    // 如果为空或者是判断是否过期
                    $ret = false;
                } else {
                    $ret = true;
                }
                break;
            case 'rm':
                $ret = DB::delete('common_process', "processid='$name' OR expiry<".time());
                break;
        }
        return $ret;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值