ファイルアクセスの排他制御のために、flockをします。書き込みと読み出しが絡んだ場合、flockを使用すると、独立に作業の待ち行列に並び,先着受付順に作業が行われます。この処理により、作業中は他の人がそのファイルに作業を行えないようになります。次にflockを使用した例を示します。

$fp = fopen('filename', 'mode');

if (flock($fp, LOCK_EX)){
    // ファイルに対する操作を行う

    flock($fp, LOCK_UN);
}else{
    print('ファイルロックに失敗しました');
}

fclose($fp);

ロックの解除を忘れていても、該当のファイルに対するハンドルをクローズするとロックが解除されます。

flockによるファイルロックとは

複数ユーザが同時にファイルを読み取っても問題はないため、ファイルを読み書きする際に全てのアクセスをブロックしてしまうと、無駄なロック解除待ちが発生します。 その無駄を省くために、flockは、排他的ロックと共有ロックの2種類を持ちます。

flockの排他的ロックでは他のロックを全てをブロックし、共有ロックでは排他的ロックのみをブロックします。 基本的に、排他的ロックは書き込みをするときに使い、共有ロックは読み込みをするときに使います。 排他的ロックでは読み込みと書き込み両方をブロックし、共有ロックでは書き込みのみをブロックします。

共有ロックを使うことで複数ユーザが同時にファイルを読み取ることができ、読み取っている間は、flocにより書き込みがブロックされます。 掲示板でいうと、記事リストを表示しようとしているユーザがいると、書き込み処理は表示が終わるまで待つ、ということになります。

flockの関数仕様

PHPにはファイルをロックするためのflock関数というものが用意されています。

bool flock ( resource $handle , int $operation [, int &$wouldblock ] )

$handle
ファイルポインタ、 $operationはロック処理の種類を指定します。 指定できる値は以下の5種です。


flockの$operationパラメータによるロック処理の種類
種類 説明
LOCK_SH 共有ロックをする。ロックされていた場合は解除されるまで待つ。
LOCK_EX 排他的ロックをする。ロックされていた場合は解除されるまで待つ。
LOCK_UN ロックを解除する。(ロックはファイルを閉じたときやスクリプト終了時に自動的に解除される。)
LOCK_SH | LOCK_NB 共有ロックをする。ロックされていた場合はfalseを返す。(Windowsではサポートされない。)
LOCK_EX | LOCK_NB 排他的ロックをする。ロックされていた場合はfalseを返す。(Windowsではサポートされない。)

$wouldblock
$operationにLOCK_NBを指定した場合にブロックされると、$wouldblockに「1」が代入され、それ以外は「0」になります。

flockによる排他的ファイル書き込みのテスト

flockを使用することで,ファイルアクセスの排他制御ができることを観察するために、次のPHPプログラムを複数のブラウザからアクセスします。flockにより排他制御を行うファイル書き込みには、10秒(sleepを使う)かけます。

$fname="flocktest2.txt";
$requestmsg="accepted message ".date('h:i:s')."\r\n";
if (($fp=fopen($fname, "a")) !== false ) { //ファイルのオープン
    if (flock($fp, LOCK_EX)==true) { //ファイルのロック 他のスレッドが使用中の場合は待たされる
        fprintf($fp,$requestmsg);
        fprintf($fp,"-- opened and locked %s\r\n",date('h:i:s')); //時刻の書き出し
        sleep(10); //ファイルをオープンしたまま10秒何もしない(動作確認のため)
        fprintf($fp,"-- %s\r\n",$this->params['url']['id']);
        fprintf($fp,"-- will be unlocked and closed %s\r\n",date('h:i:s')); //時刻の書き出し
        fflush($fp); //バッファに残っているものを書き出す
        flock($fp, LOCK_UN); //ロック解除
    } else { //ここには来ない
    }
    fclose($fp); //ファイルのクローズ
} else {//ここには来ない
}

確認手順

flockによる排他制御の確認を行うために手順を示します。上記のflockにより排他制御を行うPHPソフトを実行して、次に示すブラウザで、指定されたURLを順次入力して、実行完了後、「flocktest2.txt」の内容を確認します。
テストは、自身のブラウザの種別をパラメータにした3つのブラウザ(firefox、IE、Chrome)を立ち上げ、数秒おきにブラウザからアクセスします。アクセスが終了すると、ブラウザには、作業開始、 書き込み開始、書き込み終了の各時刻を表示し、どのような順番に作業が進んだかを確認します。

  • firefox
    http://localhost/cake/session/sessions?id=firefox
  • IE
    http://localhost/cake/session/sessions?id=IE
  • Chrome
    http://localhost/cake/session/sessions?id=Chrome

確認結果

flockにより排他制御を行った結果を示します。まず、「flocktest2.txt」の内容を次に示します。

accepted message 05:47:40
— opened and locked 05:47:40
— firefox
— will be unlocked and closed 05:47:50
accepted message 05:47:44
— opened and locked 05:47:50
— IE
— will be unlocked and closed 05:48:00
accepted message 05:47:48
— opened and locked 05:48:00
— Chrome
— will be unlocked and closed 05:48:10

flockにより排他制御の実行結果を見ると、firefoxの書き込み作業、IEの書き込み作業、Chromeの書き込み作業の順で行われ、flockによるファイル排他制御 が行われたことが確認できます。3つのブラウザ共に、読みだしたファイルの内容(ブラウザに表示されている)は3つのブラウザからの書き込みが終了した時点のものです。

書き込み作業の実行結果の時刻を見ると、flockにより排他制御が行われた詳細な作業順がさらに確認できます。

  1. 最初にfirefoxの書き込み作業が始まります。
  2. その作業中に、IEの書き込み作業、つぎにChromeの書き込み作業が、flockによる排他制御のために待ち行列に入ります。
  3. firefoxの書き込み作業が終わると、flockにより、IEの書き込み作業が待ち行列から取り出され、作業が開始されます。
  4. IEの書き込み作業が終わると、flockにより、Chromeの書き込み作業が待ち行列から取り出され、Chromeの書き込み作業が始まります。ここで待ち行列は空になります。