$fpが権限を握るflockによるファイルロック

果たして、この記事が役に立つか疑問ですが、私の様にポカをする人がいないとも限りませんし、忘備録的に書いてみます。

先日カウンターのログが頻繁に壊れると指摘があり、原因が分からず2日程考えていた時、ふと疑問が。テストしてみると、全くロックできていない。実はこう書いていました。

PHP:
  1. function lock() {
  2.   $fp = fopen('./lock', 'w');
  3.   return $fp && flock($fp, LOCK_EX);
  4. }
  5. if(!lock()) {
  6.   // ロック失敗で終了
  7. }
  8. // ログ保存
  9. $fp = fopen(LOG_FILE, 'wb');
  10. fwrite($fp, $lines);
  11. fclose($fp);

自分では正しいと思っていたんですけど、実はlock()はtrueしか返さず、後にロックが外れます。どうなっているかと言うと、lcok()と同時にロックされ真が返りますが、$fpがローカル変数で既に用済みな為、ロックが外れてしまうのです。flockによるファイルロックは$fpが生きている必要があります。つまり、以下でもロックが外れます。

PHP:
  1. $fp = fopen('./lock', 'w');
  2. flock($fp, LOCK_EX);
  3. unset($fp);

よって、こう書いて解決。
関数にしなきゃいいだけの話。

PHP:
  1. // ファイル生成可能で空にするコストがないaを指定
  2. $l_fp = fopen('./lock', 'a+');
  3. $locked = $l_fp && flock($l_fp, LOCK_EX);
  4. if(!$locked) {
  5.   // ロック失敗で終了
  6. }
  7. // ログ保存
  8. $fp = fopen(LOG_FILE, 'wb');
  9. fwrite($fp, $lines);
  10. fclose($fp);

上の例と違い、ログファイルを追記モードで扱う場合はロックファイルを用意する必要はありません。また、追記モードで書込モードの様に空にしてから書き込む事もできます。

PHP:
  1. // ファイルがなければ生成
  2. $fp = fopen(LOG_FILE, 'a+b');
  3. // ロックしてファイルを空にする
  4. $prepared = $fp && flock($fp, LOCK_EX) && ftruncate($fp, 0);
  5. if(!$prepared) {
  6.   // 準備失敗で終了
  7. }
  8. fwrite($fp, $lines);
  9. fclose($fp);

この場合、fclose($fp);する前にflock($fp, LOCK_UN);でロックを解除すると閉じる前に他の書き込みが割り込む可能性があり、好ましくありません。ロックはfclose()で解除されますし、fclose()は記述がなければスクリプト終了時にコールされます。

  • Search

Copyright (c) 2004-2008 MT312 All Rights Reserved.
Powered by WordPress ME