使用flock的PHP问题 - 文件锁定

Posted

技术标签:

【中文标题】使用flock的PHP问题 - 文件锁定【英文标题】:PHP issues using flock - file locking 【发布时间】:2013-05-23 10:26:11 【问题描述】:

我在使用 php flock() 函数时遇到问题。我需要编写两个不同的变量($O$o),但通常它不会写入第二个变量($o),可能是因为该文件连续写入两次。

代码如下:

include_once "changevar.php";
changevar("O",$seguimedia,$filename,0);
changevar("o",$offerta,$filename,0);

$seguimedia$filename$offerta 设置正确。

changevar.php:

function changevar($varname,$newval,$filename,$type)

    while(!$fp=fopen($filename,"c+"))
    
        usleep(100000);
    
    while(!flock($fp,LOCK_EX))
    
        usleep(100000);
    
    while(!include($filename))
    
        usleep(100000);
    
    ftruncate($fp,0);
    rewind($fp);
    $$varname=$newval;
    if($type==0)
    
        foreach(array("u","p","t","d") as $v)$$v=str_replace("\\","\\\\",$$v);
        $text="<?\$o=$o;\$u=\"$u\";\$c=$c;\$m=$m;\$p=\"$p\";\$C=$C;\$id=\"$id\";\$t=\"$t\";\$d=\"$d\";\$O=$O;?>";
    
    else
    
        $text="<?\$impressions=$impressions;\$clickunici=$clickunici;\$clicknulli=$clicknulli;\$creditiguadagnati=$creditiguadagnati;\$creditiacquistati=$creditiacquistati;\$creditiutilizzati=$creditiutilizzati;?>";
    
    fwrite($fp,$text);
    flock($fp,LOCK_UN);
    fclose($fp);

PHP flock() 是避免此类问题的好方法吗? 我需要使用哪种语言/功能?

【问题讨论】:

我不知道您的目标是什么,但是编写和重新读取 php 文件并不是一个好的解决方案,不仅因为大量的 i/o 流量。可能有更快更好的解决方案,memcache、sqlite、mongodb,甚至ini/csv文件都更好处理 【参考方案1】:

如果您在同一个脚本中编写两次并不重要。 但事实上,当您从两个不同的进程尝试此操作并使用文件锁定时...

无论如何,你的 changevar() 函数实际上每次都会截断文件,所以我猜这就是为什么它“似乎”只写了一个 var。

【讨论】:

【参考方案2】:

问题其实出在fopen()调用中。

您正在以c+ 模式打开文件。这意味着文件指针位于文件的开头,这将导致任何写入覆盖已经存在的内容。 雪上加霜,你打电话给ftruncate(),在写入之前将文件截断为0字节——因此,在每次写入之前,你都在手动擦除整个文件。因此,该代码保证只保留最后一次写入文件的内容,手动清除其他所有内容。

fopen() 调用应使用模式a+ftruncate()rewind() 都需要去(后者还有将文件指针放在开头的效果)。

【讨论】:

【参考方案3】:

老实说,我真的认为读取和写入 PHP 文件是一个非常非常非常糟糕的想法。如果您正在查看配置,请使用 inijson

如果你真的想读写文件,那么它可以很简单:

$file = __DIR__ . "/include/config.json";
$var = new FileVar($file);
$var['o'] = "Small o";
$var['O'] = "Big O";
$var->name = "Simple json";

echo file_get_contents($file);

输出


    "o": "Small o",
    "O": "Big O",
    "name": "Simple json"

另一个例子

// To remove
unset($var['O']);

// to update
$var['o'] = "Smaller o";

输出


    "o": "Smaller o",
    "name": "Simple json"

请注意,包含文件夹包含此.htaccess

<Files "*">
    Order Deny,Allow
    Deny from all
</Files>

为了测试这个lock 是否真的有效,我使用pthreads 来模拟竞争条件

for($i = 0; $i < 100; $i ++) 

    $ts = array();

    // Generate Thread
    foreach(range("A", "E") as $letter) 
        $ts[] = new T($file, $letter);
    

    // Write all files at the same time
    foreach($ts as $t) 
        $t->start();
    

    // Wait for all files to finish
    foreach($ts as $t) 
        $t->join();
    


// What do we have
echo file_get_contents($file);

主类

class FileVar implements ArrayAccess 
    private $file;
    private $data;
    private $timeout = 5;

    function __construct($file) 
        touch($file);
        $this->file = $file;
        $this->data = json_decode(file_get_contents($file), true);
    

    public function __get($offset) 
        return $this->offsetGet($offset);
    

    public function __set($offset, $value) 
        $this->offsetSet($offset, $value);
    

    public function offsetSet($offset, $value) 
        if (is_null($offset)) 
            $this->data[] = $value;
         else 
            $this->data[$offset] = $value;
        
        $this->update();
    

    public function offsetExists($offset) 
        return isset($this->data[$offset]);
    

    public function offsetUnset($offset) 
        unset($this->data[$offset]);
        $this->update();
    

    public function offsetGet($offset) 
        return isset($this->data[$offset]) ? $this->data[$offset] : null;
    

    private function update() 
        // Open file with locking
        $time = time();
        while(! $fp = fopen($this->file, "c+")) 
            if (time() - $time > $this->timeout)
                throw new Exception("File can not be accessed");
            usleep(100000);
        

        // Lock the file for writing
        flock($fp, LOCK_EX);

        // Overwrite the old data
        ftruncate($fp, 0);
        rewind($fp);

        // Write the new array to file
        fwrite($fp, json_encode($this->data, 128));

        // Unlock the file
        flock($fp, LOCK_UN);

        // Close the file
        fclose($fp);
    

测试类

class T extends Thread 

    function __construct($file, $name) 
        $this->file = $file;
        $this->name = $name;
    

    function run() 
        $var = new FileVar($this->file);
        $var[$this->name] = sprintf("Letter  %s", $this->name);
    

【讨论】:

以上是关于使用flock的PHP问题 - 文件锁定的主要内容,如果未能解决你的问题,请参考以下文章

PHP 利用文件锁处理高并发

PHP 利用文件锁处理高并发

如何使用 File#flock 对独占锁发出非阻塞请求?

Linux文件锁flock

使用flock的PHP问题 - 文件锁定

php原子操作,文件锁flock,数据库事务