W3CAPI 在线教程 | 菜鸟教程_LOGO
文档目录
文档目录
我的书签
 

PHP

详解PHP flock() 函数的使用

在开发中常常会遇到对文件的操作,对于初学者来说,往往关注的是对功能的实现,忽略了整个技术栈的连贯性质,因为WEB应用一般都是多线程和多进程的应用,所有的执行路径都可能同时会有多个客户端的请求,这就是多并发,如果程序中有对公共数据进行操作,那么在多并发中就要使用到锁的概念。

jesen
1
2020-03-26 20:13:53

PHP flock() 函数的定义与使用说明

一、功能说明

flock() 函数用来锁定或释放一个文件句柄(handle)也叫作文件指针。若操作成功,则返回 true。若操作失败,则返回 false。所有访问程序必须使用相同的锁定方式,如果有的没有进行锁定,有的进行了锁定,则无法保证数据的一致性。 默认情况下,此函数将阻塞,直到获得请求的锁; 这可以通过LOCK_NB选项进行控制。

二、语法结构

flock ( resource $handle , int $operation [, int &$wouldblock ] ) : bool  //支持版本(PHP 4, PHP 5, PHP 7)
 
参数 说明 使用说明
handle 文件系统指针,是典型地由 fopen() 创建的 resource(资源)  
operation 可以是以下值之一:
  1. LOCK_SH取得共享锁定(读取的程序)
  2. LOCK_EX 取得独占锁定(写入的程序
  3. LOCK_UN 释放锁定(无论共享或独占)
  4. 如果不希望 flock() 在锁定时堵塞,则使用参数 LOCK_NB(Windows 上还不支持)
  1. 经过测试“LOCK_NB”在Windows是被支持的,其他平台暂未测试,
  2. 在 PHP 5.3.2版本之前,锁也会被 fclose() 释放(在脚本结束后会自动调用),在文件资源句柄关闭时不再自动解锁。现在要解锁必须手动进行
  3. 4.0.1版本增加了常量 LOCK_XXX。 之前你必须使用 1 代表 LOCK_SH,2 代表 LOCK_EX,3 代表LOCK_UN,4 代表 LOCK_NB
wouldblock 如果锁定会堵塞的话(EWOULDBLOCK 错误码情况下),可选的第三个参数会被设置为 TRUE。(Windows 上不支持) 经过测试Windows 上确实不支持此参数,其他平台暂未测试

PHP flock() 函数的使用示例与注意事项

一、使用示例
  • 不阻塞文件锁定,如果锁定失败,则单独处理相应逻辑
$handle = fopen("j:/test.txt","r+"); //读写方式打开文件
if ( $handle ) {
    $lock = true; //windows 平台该参数无效
    if( flock($handle, LOCK_SH | LOCK_NB ,$lock) ) { //使用共享锁定,其他请求可以直接进入,LOCK_NB 为不阻塞其他进程的锁定请求,建议使用

        Log::write("锁定成功,开始执行", Log::EMERG);
        sleep(10);
        fclose($handle);

    } else {
        Log::write("锁定失败,开始执行", Log::EMERG);
    }
}
  • 自定义阻塞时间,不使用默认阻塞功能,防止程序进入死循环
$handle = fopen("j:/test.txt","r+");
if ( $handle ) {

    $start = time();
    $isLock = false;
    
    do {
        sleep(1);
        $isLock = flock($handle,LOCK_EX | LOCK_NB );
    } while ( !$isLock && (time() - $start) < 15 ); //如果请求锁定时间超过15秒则不再请求,直接处理失败

    if ( $isLock ) {

        Log::write("锁定成功,开始执行", Log::EMERG);
        sleep(10);
        fclose($handle);

    } else {
        Log::write("锁定失败,开始执行", Log::EMERG);
    }
}

二、注意事项
  • fopen函数的mode参数设定最好和flock的锁定标识参数operation进行同步,如果是fopen打开文件的模式为读,则锁定的模式也必须为读;如果打开文件的模式为写,锁定的模式为读则即使使用fwrite进行数据写入,也会失败(在使用w和w+模式打开文件时会发生,使用a和a+模式就可以写入数据,可能是跟平台有关系,测试使用的是Windows),代码如下:
$handle = fopen("j:/test.txt","w+"); //读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之
if ( $handle ) {

    $lock = true;
    if( flock($handle,LOCK_SH ,$lock) ) { //使用共享锁定,其他请求可以直接进入

        $data = fgets($handle);
        Log::write("锁定成功,开始执行,读取数据为:".$data, Log::EMERG); 
        fwrite($handle,"test"); //无法写入,而且把之前数据清空
        fclose($handle);

    } else {
        Log::write("锁定失败,开始执行", Log::EMERG);
    }
}
  • 默认情况下LOCK_SH模式锁定文件,则不会阻塞其他客户端的操作,LOCK_EX 模式的锁定会阻塞其他客户端的操作
  • 对于同一个文件的锁定,如果一个客户端已经执行了共享锁定,则其他客户端对他的独占锁定会阻塞,反之如果一个客户端执行了独占锁定,则其他客户端对它的共享读锁定会阻塞(如果使用了LOCK_NB则其他客户端会锁定失败)
相关提问
敬请期待