? ? ? ? 并发情况下,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内部调的是宿主文件系统,自然会把锁请求排队处理。
?