假设遇到这种情况,两个客户试图同时订购同一件商品(这种情况并不少见,尤其是当网站上遇到某种程度的网络堵塞的时候。)如果一个客户调用fopen函数打开一个文件并且开始写这个文件,而此时其他客户也调用了fopen函数打开这个文件并且要写这个文件,将会出现什么情况呢?文件的最终内容是什么?会是第一个订单后面就是第二个订单吗?还是恰好相反呢?订单是第一个客户的还是第二个客户的?或者将变成一些没用的东西,就像两个订单交错在一起?这些问题的答案取决于操作系统,但是,通常都是不可知的。
为了避免这样的问题,可以使用文件锁定的方法。在PHP中,文件锁定是通过flock函数来实现的。当一个文件被打开并且在进行读写操作之前,应该调用这个函数。
flock函数原型如下所示:
bool flock(resource fp,int operation[,int&wouldblock])
还必须将一个指向被打开文件的指针和一个表示所需锁定类型的常数作为参数传递给这个函数。如果文件锁定成功,其返回值为true,否则为false。如果获得文件锁将导致当前的进程被阻塞(也就是,不得不等待),可选的第3个参数将包含值true。
operation参数的可能值如表2-2所示。在PHP 4.0.1中,该参数的可能值已经发生了变化,因此表2-2给出了两个值集合。
如果打算使用flock函数,必须将其添加到所有使用文件的脚本中;否则,就没有任何意义。
请注意,flock函数无法在NFS或其他网络文件系统中使用。它还无法在其他更早不支持文件锁定的文件系统中使用,例如FAT。在某些操作系统中,它是在进程级别上实现的,因此,如果你在多线程服务器API中使用,该函数也无法正确使用。
要在这个例子中使用flock函数,我们可以对processorder.php脚本进行如下所示的修改:
$fp=fopen("$DOCUMENT_ROOT/../orders/orders.txt",'ab');
flock($fp,LOCK_EX);//lock the file for writing
fwrite($fp,$outputstring);
flock($fp,LOCK_UN);//release write lock
fclose($fp);
你还应该在vieworders.php脚本中添加如下所示的文件锁:
$fp=fopen("$DOCUMENT_ROOT/../orders/orders.txt",'r');
flock($fp,LOCK_SH);//lock file for reading
//read from the file
flock($fp,LOCK_UN);//release read lock
fclose($fp);
现在,我们的代码更加健壮,但是还不完美。如果有两个脚本同时申请对一个文件加锁,情况又会如何呢?这将导致竞争条件的问题,这两个进程将竞争加锁,但是无法确定哪一个进程将会成功,这样就会导致更多的问题。使用数据库管理系统(DBMS),我们可以很好地解决这个问题。