日期:2014-05-17  浏览次数:20727 次

PHP文件函数flock

? ? ? ? 并发情况下,PHP该如何写文件?其实这个问题不只是PHP面临的问题。不管是线程还是进程,当并发写的时候,都会遇到共享资源写冲突。软件开发过程中,写冲突无处不在,比如多线程写共享变量,比如数据库多连接并发写数据,比如多进程写文件等等。那么这些都该如何处理呢?目前普遍的处理办法就是给共享资源上独占锁(写锁)。

? ? ? ? PHP在第三版的时候就提供了一个函数flock,顾名思义,文件锁操作函数。文件锁机制是依赖于宿主文件系统的,也就是说,如何个锁法,是宿主文件系统说了算,flock只是个外壳函数,里面调用了文件系统的锁机制。

? ? ? ? 锁的话,分为两种类型,读锁和写锁。写锁也叫排它锁、独占锁,就是一个线程或进程独占资源,别的线程或进程无法使用资源,保证写数据不受干扰,不冲突;读锁也叫共享锁,允许多个线程或进程同时读,但不能有写的操作。

函数原型:

?

<?php>
flock ($handle, $operation, &$wouldblock = null);
<?>

?

参数说明:

$handle //文件指针

$operation //锁类型

$operation 有几个可用值:LOCK_EX【写锁】、LOCK_SH【读锁】、LOCK_UN【释放锁】

? ? ? ? 看个例子吧

?

<?php>
function fileWrite($file)
{
	$fp = fopen($file, 'a');
	flock($fp, LOCK_EX);//上写锁
	/*写数据*/
	fwrite($fp, "1");
	fwrite($fp, "2");
	fwrite($fp, "3\r\n");
	flock($fp, LOCK_UN);//释放锁
	fclose($fp);
}
fileWrite('D:/txt.txt');
<?>
? ? ? ? 用Jmeter做下并发测试,执行3000次请求,会发现文件里有3000行123(不考虑apache或nginx的性能瓶颈)。

?

? ? ? ? 在用Jmeter做并发测试的时候,用记事本打开这个文件,改点东西,然后点击保存,你会发现下图情况



? ? ? ? 这就是因为文件上锁了,记事本进程无法写入。

? ? ? ? 前边说了,flock函数的锁机制实际上是文件系统的锁机制,它封装了某些类型文件系统的锁机制,有些文件系统flock不支持,据PHP手册上说,FAT、NTF等这种老式文件系统和网络文件系统,flock不支持。本人没测过。

? ? ? ? PHP手册还说,flock是进程级别的,多线程的时候不起作用。可能大家就要有疑惑了,PHP又没线程的概念。那把PHP放在支持多线程的服务器上呢,大家想想看。测一测。

分析到这一步呢,会发现flock依赖宿主,对环境有要求,这就深深的伤害了代码的可移植性。怕啦!!别怕,其实伤害也没多深,flock还是能满足大部分环境的。

?

? ? ? ? 针对flock的移植性稍有些不足,大家就开始研究替代办法,我也关注了下,发现网上流传着下面这样的代码

?

<?php>
function fileWrite($file,$content)
{
	$lock = $file.'.lock';
	while (true) {
		if (file_exists($lock)) {
			usleep(100);
		}
		else
		{
			touch($lock);//上锁
			file_put_contents($file, $content, FILE_APPEND);//写文件
			@unlink($lock);//删除锁
			break;
		}
	}
}
fileWrite('D:/txt.txt','i m a phper');
<?>

? ? ? ? 你觉着这段代码有锁作用么??测测!!其实都不用测,一看就锁不住,file_exists本来就可以并行执行,当两进程同时执行到file_exists,判断所谓的锁文件不存在,结果还不是冲突着呢。

锁,实际上是将并发访问排队阻塞串行化,然后依次处理访问,上边这段代码连排队都没有,怎么能上锁呢。

? ? 这时候大家可能又会问多个进程同时执行flock,不也是并行的么?是,执行flock是并行的,这只是代表申请锁是并行的,flock内部调的是宿主文件系统,自然会把锁请求排队处理。

?