2013-01-24 30 views
7

我有一个文件名数组,每个进程只需要创建和写入一个文件。以线程安全的方式创建文件

这就是我来:

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 
} 

但我不喜欢这样,我依靠filesize

任何建议以另一种(更好)的方式做到这一点?

UPD:添加标签,轻松地讨论

UPD 2:我使用filesize,因为我没有看到任何其他可靠的方法来检查,如果当前线程创建的文件(因此,它是空的)

UPD 3:该解决方案应该条件种族免费。

+0

你试图通过这样做解决什么问题? – cgTag

+0

@Mathew Foscarini:它管理没有并发同步的第三方资源。 – zerkms

回答

3

一种可能,稍微丑陋的解决方案将锁定一个锁文件,然后测试文件是否存在:

$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');将创建该文件。

+0

是的,这可以工作(同步文件的同步文件 - “我们需要更深入”);-) +1 – zerkms

+0

但不能泄漏锁定,如果它在临界区崩溃oO – Dmitry

2

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

  1. 创建临时文件
  2. 做就可以
  3. rename到新的位置,如果位置的所有必要的文件操作不存在。

从技术上讲,由于rename将覆盖目标,所以并发线程仍然有可能发生冲突。这是非常不可能的,如果您有:

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

你可以使用md5_file验证文件内容,该块之后是正确的。

+0

“如果位置不存在,则将其重命名为新位置。” ---你如何以线程安全的方式检查它? “有机会” - 我不想依靠“机会”。 “这是不太可能的”---我不想依赖“可能与否”,我希望能够保证**的解决方案始终按预期工作。 – zerkms

+0

那么你有没有条件竞争的提案?现在这不是一个答案,对不起。 – zerkms

+0

我喜欢这个建议;它使关键部分很小。 – Dmitry

1

可以保证使用semaphores独占访问(仅适用于UNIX,并且所提供的sysvsem扩展安装):

$s = sem_get(ftok($filename), 'foo'); 
sem_acquire($s); 

// Do some critical work... 

sem_release($s); 

否则,你也可以使用flock。它不需要任何特殊的扩展,但根据comments on PHP.net比使用信号灯慢一点:

$a = fopen($file, 'w'); 
flock($a, LOCK_EX); 

// Critical stuff, again 

flock($a, LOCK_UN); 
+0

如果你检查我的问题,你会看到,我已经在使用'flock' – zerkms

+0

Ouch,你是对的...... :)那么使用信号灯怎么样? – helmbert

+0

他们将导致相同的问题 - 如何检查文件是否由当前进程创建。我不知道如何使用信号量来同步'file_exists' – zerkms

0

在fopen调用中使用模式'x'而不是'c'。并检查生成的$ fp,如果它为假,该文件不是由当前线程创建的,并且应该继续到下一个文件名。如果fopen($ VMidFile,'x')无法创建该文件,因为它已经存在,您可能希望在fopen调用前放置一个@以禁止任何警告。

这应该工作,即使没有羊群。

+0

如果脚本死亡并且不清除文件会怎么样?这需要人工交互来清理文件以再次运行,不是吗? – zerkms

+0

不知道这是如何涉及到原始问题,你是什么意思“清理文件”?之后删除它,或..?只要您可以定义确定另一个线程是否仍在运行并处理该文件的确切条件,或者它是否崩溃以及文件是否已成为孤立文件,那么它可以自动处理。 – Rogier

+0

所以这就是最初的问题:“只要你可以定义确定另一个线程是否仍在运行的确切条件”---这就是我问到的锁定机制。该机制应该是可靠的,以便它不需要任何额外的启发式。根据您的提议,我发现如果某个进程死亡并且不会删除该文件,算法将会卡住。 – zerkms