2013-01-24 9 views
7

ファイル名の配列があり、各プロセスは1つのファイルのみを作成して書き込む必要があります。スレッドセーフな方法でファイルを作成する

これは私がに来たものです:

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:(現在のスレッドがファイルを作成した場合、私がチェックする他の信頼できる方法が表示されていないので、私はfilesizeを使用しています:簡単に

UPD 2を議論するためにラベルを追加しましたまだ空です)

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

あなたは(UNIXのみ、そしてsysvsem拡張機能がインストールされて提供された)semaphoresを使用して排他的アクセスを確保することができます

$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

あなたが私の質問をチェックすると、私はすでに '群れ 'を使用していることがわかります – zerkms

+0

あなたは正しいです... :)だから、セマフォーを使うのはどうですか? – helmbert

+0

彼らは同じ質問につながります - ファイルが現在のプロセスによって作成されたかどうかを確認する方法。私は 'file_exists'をセマフォと同期させる方法を理解していません – zerkms

0

fopenコールで 'c'の代わりに 'x'を使用します。そして結果として得られる$ fpをチェックし、もし偽であれば、ファイルが現在のスレッドによって作成されていないので、次のファイル名に進むべきです。

また、fopen($ VMidFile、 'x')がすでに存在しているためにファイルを作成できない場合、警告を抑制するために、PHPのインストール設定に応じて、@をfopen呼び出しの前に置くことができます。

これは群れなしでも機能するはずです。

+0

スクリプトが終了してファイルをクリーンアップしないとどうなりますか?それは再び実行するために、ファイルをきれいにする人間のやりとりを必要とするでしょうか? – zerkms

+0

これが元の問題とどのように関連しているのかわからない場合は、「ファイルをクリーンアップする」とはどういう意味ですか?後でそれを削除するか、または..?別のスレッドがまだ実行されているかどうかを判断する正確な条件を定義できる場合、またはクラッシュしてファイルが孤立している場合は、自動的に処理できます。 – Rogier

+0

これは最初の質問です。「別のスレッドがまだ実行されているかどうかを判断する正確な条件を定義できれば---ロック機構について質問しました。また、メカニズムは信頼性が高く、追加のヒューリスティックを必要としません。私の提案では、プロセスが死んでファイルが削除されないと、アルゴリズムが停止することがあります。 – zerkms