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
版权声明:本文标题:Php flock 多线程,php 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/free/1699330663h343054.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论