PHP并发读写文件的解决方案

在并发高的情况下,操作同一个文件会导致数据错乱,所以需要在操作文件时进行一些特殊处理,下面总结一下几种解决方案。

方案一:使用flock函数对文件加锁

/*  
* flock(file,lock,block)  
* file 必需,规定要锁定或释放的已打开的文件  
* lock 必需。规定要使用哪种锁定类型。  
* block 可选。若设置为 1true,则当进行锁定时阻挡其他进程。  
* lock  
* LOCK_SH 要取得共享锁定(读取的程序)  
* LOCK_EX 要取得独占锁定(写入的程序)  
* LOCK_UN 要释放锁定(无论共享或独占)  
* LOCK_NB 如果不希望 flock() 在锁定时堵塞  
/*  

// 获取锁
if (flock($file,LOCK_EX)) {
    // 操作文件
    fwrite($file,'write more words');  
    // 操作完毕后释放锁
    flock($file,LOCK_UN);  
} else {  
    //处理错误逻辑  
}  
fclose($file);  

flock函数在多并发情况下,似乎会经常独占资源,不即时释放,或者是根本不释放,造成死锁,从而使服务器的cpu占用很高,甚至有时候会让服务器彻底死掉。所以单纯的使用flock加锁并不能完全解决问题。

方案二:限定加锁时间,超时则退出

if($fp = fopen($fileName,'a')) {
    $startTime = microtime();
    do{
        $canWrite = flock($fp, LOCK_EX);
        if(!$canWrite) {
            usleep(round(rand(0, 100) * 1000));   // 释放cpu,将cpu资源先让给其他进程
        }
        // 如果未获取到锁,且未超时,则继续获取锁
    } while((!$canWrite) && ((microtime() - $startTime) < 1000));
    if($canWrite) {
        fwrite($fp, $dataToSave);
        flock($file,LOCK_UN);  
    }
    fclose($fp);
}

方案三:使用临时文件

$dir_fileopen='tmp';

function cfopen($filename,$mode){
    global $dir_fileopen;
    clearstatcache();
    // 创建一个临时文件
    do{
        $id=uniqid();
        $tempfilename=$dir_fileopen.'/'.$id.md5($filename);
    } while(file_exists($tempfilename));
    // 将要操作的文件内容拷贝到临时文件中
    copy($filename,$tempfilename);
    $fp = fopen($tempfilename, $mode);
    return $fp ? [$fp, $filename, $id, @filemtime($filename)] : false;
}

function cfwrite($fp,$string){
    // 将新增内容写入到临时文件中
    return fwrite($fp[0], $string);
}

function cfclose($fp){
    global $dir_fileopen;
    $success = fclose($fp[0]);
    clearstatcache();
    $tempfilename = $dir_fileopen.'/'.$fp[2].md5($fp[1]);

    // 如果要操作的文件在操作期间没有被修改过,则说明没有人操作过该文件,那么将临时文件改名为真正的文件
    if((@filemtime($fp[1]) == $fp[3])){
        rename($tempfilename,$fp[1]);
    }else{
        //说明有其它进程在操作目标文件,当前进程被拒绝,删除临时文件
        unlink($tempfilename);
        $success = false;
    }
    return $success;
}

$startTime = microtime();
do{
    $fp=cfopen('lock.txt','a+');
    cfwrite($fp,"welcome to beijing.\n");
    $success = cfclose($fp, 'on');
    if(!$success) {
        usleep(round(rand(0, 100) * 1000));   // 释放cpu,将cpu资源先让给其他进程
    }
}while(!$success && ((microtime() - $startTime) < 1000));    // 如果为false,说明操作失败,则重新进行一次操作

方案四:使用队列

创建一个文件操作的队列,然后写一个脚本从队列中依次读取文件操作的信息再对文件进行相应的操作,这样每次就只有一个进程在操作文件,就解决了并发的问题。

以上内容参考自:http://blog.csdn.net/daiyan_csdn/article/details/51524781

你可能感兴趣的:(【语言】,----------PHP)