Php 以线程安全的方式创建文件

Php 以线程安全的方式创建文件,php,thread-safety,locking,file-locking,Php,Thread Safety,Locking,File Locking,我有一个文件名数组,每个进程只需要创建和写入一个文件 这就是我的目的: foreach ($filenames as $VMidFile) { if (file_exists($VMidFile)) { // A continue; } $fp = fopen($VMidFile, 'c'); // B if (!flock($fp, LOCK_EX | LOCK_NB)) { // C continue; }

我有一个文件名数组,每个进程只需要创建和写入一个文件

这就是我的目的:

foreach ($filenames as $VMidFile) {
    if (file_exists($VMidFile)) { // A
        continue;
    }

    $fp = fopen($VMidFile, 'c'); // B

    if (!flock($fp, LOCK_EX | LOCK_NB)) { // C
        continue;
    }

    if (!filesize($VMidFile)) { // D
        // write to the file;

        flock($fp, LOCK_UN);
        fclose($fp);
        break;
    }

    flock($fp, LOCK_UN);
    fclose($fp); // E
}
但我不喜欢这样,我依赖的是
文件大小

有没有其他(更好)方法的建议

UPD:添加标签以方便讨论

UPD2:我正在使用
filesize
,因为我看不到任何其他可靠的方法来检查当前线程是否创建了该文件(因此它仍然是空的)


UPD 3:解决方案应该是无种族条件的。

而不是创建一个文件并希望它不受干扰:

  • 创建一个临时文件
  • 对其执行所有必要的文件操作
  • 如果位置不存在,则将其发送到新位置
  • 从技术上讲,由于
    rename
    将覆盖目标,因此并发线程仍有可能发生冲突。如果您有:

    if(!file_exists($lcoation) { rename(...
    

    您可以使用来验证此块后的文件内容是否正确。

    您可以使用(仅限UNIX,并且前提是安装了
    sysvsem
    扩展名):

    否则也可以使用。它不需要任何特殊扩展,但根据,它比使用信号量慢一点:

    $a = fopen($file, 'w');
    flock($a, LOCK_EX);
    
    // Critical stuff, again
    
    flock($a, LOCK_UN);
    

    一个可能的、稍微难看的解决方案是锁定一个锁定文件,然后测试该文件是否存在:

    $lock = fopen("/tmp/".$filename."LOCK", "w"); // A
    
    if (!flock($lock, LOCK_EX)) { // B
        continue;
    }
    if(!file_exists($filename)){ // C
        //File doesn't exist so we know that this thread will create it
        //Do stuff to $filename
        flock($lock, LOCK_UN); // D
        fclose($lock);
    }else{
        //File exists. This thread didn't create it (at least in this iteration).
        flock($lock, LOCK_UN);
        fclose($lock);
    }
    

    这应该允许以独占方式访问该文件,还允许决定是否调用
    fopen($VMidFile,'c')
    将创建文件。

    在fopen调用中使用模式“x”而不是“c”。并检查结果$fp,如果为false,则该文件不是由当前线程创建的,您应该继续使用下一个文件名

    此外,根据PHP的安装设置,如果fopen($VMidFile,'x')由于文件已经存在而无法创建该文件,则可能需要在fopen调用前面加上@,以抑制任何警告


    即使没有flock,这也应该可以工作。

    你想通过这样做解决什么问题?@Mathew Foscarini:这是管理没有并发同步的第三方资源。“如果位置不存在,请将其重命名为新位置。”---你将如何以线程安全的方式进行检查?“有机会”——我不想依赖“机会”。“那是非常不可能的”——我不想依赖“可能或不可能”,我想要的解决方案保证它始终按预期工作。那么你有没有一个无条件竞争的提案?现在这不是一个答案,对不起。我喜欢这个建议;如果你检查我的问题,你会发现,我已经在使用
    flock
    ,它们将导致相同的问题-如何检查文件是否由当前进程创建。我不知道如何将
    文件\u exists
    与信号量同步。我首先获取一个信号量,然后在已确保独占访问的情况下检查文件是否存在;否则,当前线程/进程肯定会创建它。这是阻塞解决方案。的确,它会工作,但它会阻止一切是的,这可能会工作(同步文件的同步文件-“我们需要更深入”);-)+1但是如果它在关键部分o中崩溃,这难道不会泄漏锁吗?如果脚本死掉并且没有清理文件,会发生什么情况?清理文件需要人工交互才能再次运行,不是吗?不确定这与原始问题有何关系,你说的“清理文件”是什么意思?以后删除它,或者。。?它可以自动处理,只要您能够定义确定另一个线程是否仍在运行并处理该文件的确切条件,或者它是否崩溃并且该文件已成为孤立文件的条件。这就是最初的问题:“只要您能够定义确定另一个线程是否仍在运行的确切条件“---这就是我所问的关于锁定机制的问题。该机制应该是可靠的,因此不需要任何额外的启发式。根据你的建议,我发现如果一个进程死了,并且没有删除文件,那么这个算法就会陷入困境。“x”模式会考虑所有的线程安全和竞争条件,它本身基本上是一个锁机制。确切地说,它会在什么时候或什么地方被卡住?进程终止,文件留在那里。无法再次运行任何进程,因为
    x
    将永远不会“获取锁”
    $lock = fopen("/tmp/".$filename."LOCK", "w"); // A
    
    if (!flock($lock, LOCK_EX)) { // B
        continue;
    }
    if(!file_exists($filename)){ // C
        //File doesn't exist so we know that this thread will create it
        //Do stuff to $filename
        flock($lock, LOCK_UN); // D
        fclose($lock);
    }else{
        //File exists. This thread didn't create it (at least in this iteration).
        flock($lock, LOCK_UN);
        fclose($lock);
    }