php flock失效问题

2018-02-24 09:50:28来源:https://yuerblog.cc/2018/02/14/php-flock-not-working/作者:鱼儿的博客人点击

分享

这两天给自己的业余项目写了一个方法,用来避免crontab调度的PHP脚本并发执行。


做法

一般通过使用文件锁flock方法,令相同的PHP脚本采用非阻塞锁同一个磁盘文件,如果文件被占用则会报错,从而可以脚本立即退出。


现象

但实践中发现,在controller文件中直接flock是可以实现的,当把flock的逻辑封装到其他文件的一个函数中后就失效了。


原因

调试了半天,突然想起来以前就遇到过这个神坑。。


错误代码如下:


class Crontab
{
    /**
     * 确保任务没有并发执行
     */
    public static function isRunning() {
        global $argv;
 
        $ident = [];
        foreach ($argv as $idx => $value) {
            $ident[] = $idx . '=' . urlencode($value);
        }
        $ident = md5(implode('&', $ident));
 
        $lockDir = /Yii::getAlias('@app/runtime/crontab/');
 
        @mkdir($lockDir, 0755, true);
 
        $file_lock = fopen($lockDir . $ident, 'w+');
 
        $wouldBlock = 0;
        flock($file_lock, LOCK_EX | LOCK_NB, $wouldBlock);
 
        return $wouldBlock;
    }
}

根据命令行参数生成唯一hash值,代表该PHP任务。


创建锁文件,执行flock非阻塞锁,返回wouldBlock标识锁是否已被占用。


我在脚本入口调用了Crontab::isRunning()方法,发现并发启动脚本后,总是能获得锁。


错误原因是:isRunning()方法退出后,$file_lock没有继续使用,被PHP垃圾回收,$fp文件句柄关闭导致锁自动释放。


解决
class Crontab
{
    /**
     * 保存起来避免被php作为垃圾回收
     * @var null
     */
    static $file_lock = null;
 
    /**
     * 确保任务没有并发执行
     */
    public static function isRunning() {
        global $argv;
 
        $ident = [];
        foreach ($argv as $idx => $value) {
            $ident[] = $idx . '=' . urlencode($value);
        }
        $ident = md5(implode('&', $ident));
 
        $lockDir = /Yii::getAlias('@app/runtime/crontab/');
 
        @mkdir($lockDir, 0755, true);
 
        self::$file_lock = fopen($lockDir . $ident, 'w+');
 
        $wouldBlock = 0;
        flock(self::$file_lock, LOCK_EX | LOCK_NB, $wouldBlock);
 
        return $wouldBlock;
    }
}

确保在整个PHP生命期内,文件句柄都不会被释放即可,所以保存在类静态成员变量里。


最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台