English

File#flock

web page のカウンタや掲示板のデータが壊れているのって, 見たことない ですか? 昔, 自作のカウンタを付けていたときは, 極たまにゼロにリセットさ れていることがありました. クラックされたってのは考えにくいので, 多分ファ イル操作がうまくいかなくて, データが失われてしまったのかと思います.

こういう「壊れたデータ」を見る機会が増えると, 「計算機でもミスるこ とがあるんだな」という雰囲気が蔓延してきているように感じるのですが, ちょっ と待て, 果してそういうものなんでしょうか? 知識として flock という関数があることは知ってましたが, 正しい使い方 を知りませんでした. なので, 正しい flock の使い方を調べて, それで正しく動くかどうかテストしてみました.

やることは web page のカウンタと同じで, 以下の通りです.

  1. ファイルから数字を読む
  2. 数字を +1 する
  3. ファイルに数字を保存する

これを, 複数のスレッドで同時に行い, きちんと動作するかを調べます. 上の作業に flock を加えてもう少し詳しく書くと, 次のように なります.

  1. ファイルを読み書き可能 (r+) で開く
  2. ファイルをロック (exclusive lock, 排他ロック)
  3. ファイルから数字を読む
  4. 数字を +1 する
  5. ファイルポインタを先頭へ移動する
  6. ファイルに数字を保存する
  7. ファイルを閉じる (アンロックは暗黙に行われる)

ファイルを閉じる前に明示的にアンロックすると, もしファイルに書き出 されていないデータがバッファに残っているときに, マズイことが起こるそう です. もし明示的にアンロックするなら, その前にバッファをフラッシュする 必要があるそうです.

以上を元にして作ってみたスクリプトが, 次のものです. $number_of_threads 個のスレッドを作り, 各スレッドでは $number_of_iteration 回だけ数えます. 最終的に表示される数 字が, 両者の積に一致すれば OK. 実行にあまりにも時間がかかるときには, $number_of_iteration の数字を減らしてみて下さい.

#!/usr/local/bin/ruby
# flock00.rb : test for flock
# 1999/12/20 11:28:15 msato
# $Id: flock.uhtml,v 1.3 2000-01-04 17:28:36+09 msato Exp $

require 'thread'

$fname = 'count.dat'
$number_of_threads = 5
$number_of_iteration = 3000
$indent = "\t"

def count(num)
  $number_of_iteration.times do
    open($fname, "r+") do |f|
      f.flock(File::LOCK_EX) # <==== lock
      count = f.gets.to_i + 1
      f.rewind
      f.print count
#      f.flock(File::LOCK_UN) # <==== unlock
      $stderr.print $indent * num, count, "\n"
    end
  end
end

begin
  open($fname, "w") do |f|
    f.print "0"
  end
rescue
  $stderr.print "Cannot open #{fname}\n"
  exit 1
end

th = []
0.upto ($number_of_threads - 1) do |num|
  th[num] = Thread.start { count(num) }
end

0.upto ($number_of_threads - 1) do |num|
 th[num].value
end

print "Count = #{$number_of_threads * $number_of_iteration}\n"
File::delete($fname)

ファイル操作は count 関数で行ってます. ここの f.flock(File::LOCK_EX)f.flock(File::LOCK_UN) をいじって試してみて下さい. 私が試 したところでは, 結果は次の通りでした.

明示的なアンロックをしても動作に問題がなかったのは, データが高々数 バイトという小さなものだったからですかねぇ…

そうそう, 全てのスレッドが終了するまで待つために, ここでは, スレッ ドが停止するまで待つ Thread#value を評価しています. 他に 方法はあるのでしょうか?

などと書いておきながら, マニュアルをよく読んで見るとそのものずばり の Thread#join を発見 (^^; でも, なんで join なんだろう…



[うさぎ印] おたより, お待ちしています

sato.mshr@gmail.com