在上篇的文章中,我们了解了为什么需要锁,以及锁的应用场景。
那么,该怎么用锁来进行并发业务逻辑呢?
在php中,已经提供了关于锁的函数 flock
flock(file,lock,block)
参数
描述
file
必需。规定要锁定或释放的已打开的文件。
lock
必需。规定要使用哪种锁定类型。
可能的值:
- LOCK_SH - 共享锁定(读取的程序)。允许其他进程访问该文件。
- LOCK_EX - 独占锁定(写入的程序)。防止其他进程访问该文件。
- LOCK_UN - 释放一个共享锁定或独占锁定
- LOCK_NB - 锁定的情况下避免阻塞其他进程。
block
可选。若设置为 1,则当进行锁定时阻塞其他进程。
首先,我们要分清楚,锁有2种,共享锁,以及独占锁
共享锁
共享锁用于某个文件不会被写,或者不会被更新(也就是只读)的情况,加了共享锁的文件,只能再加共享锁,而不能加独占锁
例如:
$file = fopen('1.txt','r'); if (flock($file,LOCK_SH)){ //读取一次文件的数据 $data = fread($file,1); var_dump($data); echo "加锁成功\\n"; //阻塞一次,方便运行另一个测试文件 fread(STDIN,1); //文件指针重置 fseek($file,0); //继续获取 $data = fread($file,1); var_dump($data); }
运行2次该代码,正常上锁并正常读取数据:
同样,如果在上了共享锁的情况,增加独占锁,则该进程会阻塞,直到共享锁释放:
<?php $file = fopen('1.txt','r'); if (flock($file,LOCK_EX)){ $data = fread($file,1); var_dump($data); echo "加锁成功"; fread(STDIN,1); }
需要所有的共享锁文件释放,才可加锁成功:
同理,如果是先加了独占锁,则共享锁会被阻塞,不做详细说明
注意事项
共享锁加上之后,虽然不能再加上独占锁进行独占写入,但是还是会被未加锁的进程影响,所以注意,当你确定某个文件是只读,或者说读取的时候不被写入影响时,关于操作这个文件的代码,都需要增加锁,不管是独占锁还是共享锁,否则数据还是会出现问题
例如:
当加好共享锁之后,运行这段代码:
<?php $file = fopen('1.txt','r+'); fwrite($file,2,1);
数据将会出现变动:
独占锁
独占锁用于数据可能会被修改的文件,当一个进程加上独占锁之后,其他进程将不能增加独占锁和共享锁(将会阻塞)
测试代码:
<?php $file = fopen('1.txt','r+'); if (flock($file,LOCK_EX)){ echo "加锁成功\\n"; //读取一次文件的数据 $data = fread($file,11); var_dump($data); //读取一次文件的数据 $time = time(); //文件指针重置 fseek($file,0,SEEK_SET ); $data = fwrite($file,$time,strlen($time)); //阻塞一次,方便运行另一个测试文件 fread(STDIN,11); //文件指针重置 fseek($file,0,SEEK_SET ); //继续获取 $data = fread($file,11); var_dump($data); }
当进程1运行该代码时,进程2将会阻塞,无法加锁:
同样,共享锁也会出现一样的情况(不做测试)
注意事项
独占锁加上之后,虽然其他进程无法加共享锁以及独占锁,但是可以直接写入文件,同样会造成数据污染,所以注意,当你确定某个文件确定需要加锁时,关于操作这个文件的代码,都需要增加锁,不管是独占锁还是共享锁,否则数据还是会出现问题
<?php $file = fopen('1.txt','r+'); $result = fwrite($file,'65555555'); var_dump($result); var_dump(fread($file,11));
其他
非阻塞
通过在flock函数中的参数中增加LOCK_NB,可以避免阻塞,当不能加锁时会直接返回失败:
<?php $file = fopen('1.txt','r+'); if (flock($file,LOCK\_EX|LOCK\_NB)){ echo "加锁成功\\n"; }else{ echo "加锁失败"; }
并发解决
还记得第一篇并发锁的文章吗?通过这个方式,就可以实现同一个文件在同一时间自有一个进程访问了