admin 管理员组

文章数量: 887021

Php flock 多线程,php

场景

php进程锁可以解决很多业务场景.

典型的比如crontab脚本防重叠

比如做搞并发计数器类似的写数据操作行为可以用

方法

flock(file,lock,block)

参数

描述

file

必需。规定要锁定或释放的已打开的文件。

lock

必需。规定要使用哪种锁定类型。

block

可选。若设置为 1 或 true,则当进行锁定时阻挡其他进程。

说明

flock() 操作的 file 必须是一个已经打开的文件指针。

lock 参数可以是以下值之一:

要取得共享锁定(读取的程序),将 lock 设为 LOCK_SH(PHP 4.0.1 以前的版本设置为 1)。

要取得独占锁定(写入的程序),将 lock 设为 LOCK_EX(PHP 4.0.1 以前的版本中设置为 2)。

要释放锁定(无论共享或独占),将 lock 设为 LOCK_UN(PHP 4.0.1 以前的版本中设置为 3)。

如果不希望 flock() 在锁定时堵塞,则给 lock 加上 LOCK_NB(PHP 4.0.1 以前的版本中设置为 4)。

注意"加上"两个字!很多人没有理解加上是什么意思导致代码写错,我来举个代码实例相信大家都看得懂

flock($f, LOCK_EX) //单单LOCK_EX是会进行阻塞的

flock($f, LOCK_EX | LOCK_NB) //加上了LOCK_NB,则就不会阻塞了

LOCK_NB非阻塞有什么优点呢,正常情况下我们的业务场景是不可能使用阻塞的,因为万一一个进程卡住了,后面所有进来的进程得跟着全部阻塞在那里,到时候进程泛滥后果不堪设想!所以正常情况下我们都是加上非阻塞,然后程序里面去追加啊超时机制,防止进程泛滥的场景LOCK_NB非阻塞有什么优点呢,正常情况下我们的业务场景是不可能使用阻塞的,因为万一一个进程卡住了,后面所有进来的进程得跟着全部阻塞在那里,到时候进程泛滥后果不堪设想!所以正常情况下我们都是加上非阻塞,然后程序里面去追加啊超时机制,防止进程泛滥的场景

优点

如果没有显式的解锁,在php进程结束或者异常断开的时候会自动进行释放操作.(假设我进程异常跳出或者被kill掉,就算我没有释放进程锁flock($f, LOCK_UN),它也会自动进行释放操作,下个进程进来的时候还是可以进来,这样就规避了业务上某个进程异常断开,导致后面的进程一直进不来的尴尬场景)

注意了特别再次说明这条,在实际业务场景我们会对业务代码进行封装,假设我们封装了锁机制的方法要注意坑了,在function结束调用的时候,flock就会隐式的调用flock($f, LOCK_UN),这样我们的进程锁就会被释放了!所以解决办法两种:

1.代码写在调用的最外层入口(这个基本不符合实际)

2.代码封装起来用callback传递业务代码,下面案例会有举例

缺点

在某些操作系统上,flock()是在进程级别实现的。当使用多线程服务器API(如ISAPI)时,您可能无法依赖flock()来保护文件免受在同一服务器实例的并行线程中运行的其他PHP脚本的影响!

flock()在过时的文件系统(如FAT及其派生文件)上不受支持,因此在这些环境下总是返回FALSE

在php方法调用结束的时候锁会被隐式销毁,要注意这个坑,这个会导致代码很难封装,下面的案例会举例展示.如何解决下面案例也会有举例

案例

最简单的案例

$file = "/tmp/test.flock1";

$f = fopen($file, 'a');

if($f === FALSE){//没有权限

echo '文件没有写入权限!';

exit;

}

while(TRUE){

if(!flock($f, LOCK_EX | LOCK_NB)){//追加 LOCK_NB 以非阻塞的模式进行

//判断是否有正在进行的进程

echo '已有进程在执行!' . "\n";

sleep(1);

//正常业务会在加个超时等待,比如等待多久就结束进程,防止进程泛滥

} else{

echo '开始运行';

sleep(100);

flock($f, LOCK_UN);

fclose($f);

// unlink($file);

注意不能追加删除文件否则会出现多个进程并发的场景,举个例子,

//我开了第一个进程,正在运行了,然后开了第二个进程进行了等待,

//此时我删除了这个文件,然后第二个进程开始运行了,

//第三个进程刚好进来就可以运行了,此时就有两个进程同时运行了

}

}

echo '结束';

实际业务场景遇到的坑调用方法结束后flock会被隐式释放

class flockClass

{

public function run()

{

self::lock();

sleep(10);

echo '运行结束' . "\n";

}

static public function lock()

{

$file = '/tmp/lock.log';

$f = fopen($file, 'a');

if($f === FALSE){//没有权限

echo '文件没有写入权限!';

exit;

}

for($i = 1; $i <= 25; $i++){

if(!flock($f, LOCK_EX | LOCK_NB)){//追加 LOCK_NB 以非阻塞的模式进行

//判断是否有正在进行的进程

echo '已有进程在执行!' . "\n";

sleep(1);

} else{

echo '开始运行' . "\n";

// flock($f, LOCK_UN);

// fclose($f);

break;

}

}

}

}

$m = new flockClass();

$m->run();

执行上面脚本会发现锁进制根本没法生效,原因在于,flock在方法调用完之后就会被隐式释放,所以如果有人尝试使用构造方法construct,和析构方法destruct来实现,会发现锁机制根本没办法生效,原因就是如此.

接下来演示如何在实际业务中使用的案例(使用callback解决实际业务的问题)

class flockClass

{

public function run()

{

$callback = function () {

echo "11111\n";

sleep(10);

};

self::lock($callback);

echo '运行结束' . "\n";

}

static public function lock($callback)

{

$file = '/tmp/lock.log';

$f = fopen($file, 'a');

if($f === FALSE){//没有权限

echo '文件没有写入权限!';

exit;

}

for($i = 1; $i <= 25; $i++){

if(!flock($f, LOCK_EX | LOCK_NB)){//追加 LOCK_NB 以非阻塞的模式进行

//判断是否有正在进行的进程

echo '已有进程在执行!' . "\n";

sleep(1);

} else{

echo '开始运行' . "\n";

$callback();

flock($f, LOCK_UN);

fclose($f);

break;

}

}

}

}

$m = new flockClass();

$m->run();

结语

有人说既然是用文件锁,那么我干嘛不自己写个简单的脚本使用文件锁也可以实现锁机制,确实如此可以这么干,但是自己写的文件锁有个弊端就是,假如某个进程是异常断开那么会导致一直持续死锁状态,使用flock就没有这个问题

本文标签: Php flock 多线程 php