Entries

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
この記事に対してトラックバックを送信する(FC2ブログユーザー)
http://kovayashi.blog120.fc2.com/tb.php/80-5c7f1ef4

-件のトラックバック

-件のコメント

コメントの投稿

投稿フォーム
投稿した内容は管理者にだけ閲覧出来ます

ruby flock

マルチプロセスで一つのファイルに対して書き込みを実行すると、
タイミングによっては、問題が発生する。

例えば、
プロセスAで、以下を実行し、

f=open('./hoge.txt','w')
f.puts 'aaa'; f.flush
f.puts 'aaa'; f.flush
f.close

プロセスBで以下を実行したとする。

f=open('./hoge.txt','w')
f.puts 'bbb'; f.flush
f.close

時系列的な各行の実行が以下に示す???の順番だった場合、

?f=open('./hoge.txt','w') #@プロセスA
?f.puts 'aaa'; f.flush #@プロセスA
?f=open('./hoge.txt','w') #@プロセスB
?f.puts 'bbb'; f.flush   #@プロセスB
?f.puts 'aaa'; f.flush   #@プロセスA
?f.close  #@プロセスA
?f.close  #@プロセスB

ファイル./hoge.txtの内容は、

bbb
aaa

となり、プロセスA、B共に期待した結果が得られない。

そこで、flock()を使って、この問題を解決してみる。

flock()は、Fileクラスのオブジェクトが必要である。
なので、open()してからでないと、flock()は使えない。

f=open('./hoge.txt','w')
f.flock(File::LOCK_EX|File::LOCK_NB)

しかし、これでは、'w'モードでopen()した時には、ファイルは空になってしまうので、
意味がない。そこで、先ずは、'r'モードでopen()してロックを取るようにする。

f=open(file,'a')
f.close
r=open(file,'r')
ret=r.flock(File::LOCK_EX|File::LOCK_NB)
if ret==0 then
f=open(file,'w')
else
puts 'file is locked'
r.close
exit
end

※行頭2行の説明
 『先ずは、'r'モードでopen()』と説明しておきながら、上記例では、先ずは'a'モードでopen()している。
 これは、ファイルが存在しない場合、'r'モードのopen()に失敗するので、いったん'a'モードで
 open()→closeする。(touchするかんじ。)

この様な方法で問題は解決したかに思えたが、Windows環境で試してみると、
write後のflushで、エラーが出てしまう。


Permission denied - ./hoge.txt (Errno::EACCES)

(flushを廃止した場合、closeで同様のエラー)

そこで、'r+'モードでopen()後、ロック取得を試みるようにして、
ロック獲得に場合は、truncate()でファイルポインタを先頭に戻してやる事にする。

f=open(file,'a')
f.close
f=open(file,'r+')
ret=f.flock(File::LOCK_EX|File::LOCK_NB)
if ret==0 then
f.truncate(0)
puts 'i have a lock'
else
puts 'file is locked'
f.close
exit
end

これなら、unixでもWindowsでも動いた。

以下テストに使ったツール。

#!/usr/local/bin/ruby
file='./hoge.txt'

buf=''
f=nil
r=nil

loop do
STDOUT.write '>'
buf=STDIN.gets.chomp

case buf
when 'ow'
f=open(file,'a')
f.close
f=open(file,'r+')
ret=f.flock(File::LOCK_EX|File::LOCK_NB)
if ret==0 then
f.truncate(0)
puts 'i have a lock'
else
puts 'file is locked'
f.close
exit
end

when 'ou'
a=open(file,'a')
a.close
r=open(file,'r')
ret=r.flock(File::LOCK_EX|File::LOCK_NB)
if ret==0 then
f=open(file,'w')
puts 'file open'
else
puts 'file is locked'
r.close
exit
end

when 'c'
f.close if f
r.close if r
puts 'close'

when 'e'
exit

else
if /^w *(.*)/=~buf then
f.puts $1
f.flush
else
puts 'no such a command'
end
end
end

この記事に対してトラックバックを送信する(FC2ブログユーザー)
http://kovayashi.blog120.fc2.com/tb.php/80-5c7f1ef4

0件のトラックバック

0件のコメント

コメントの投稿

投稿フォーム
投稿した内容は管理者にだけ閲覧出来ます

Appendix

プロフィール

kovayashi

Author:kovayashi
・嫌いなもの
 →好きなもの

・インストール
 →バンドル
・バックアップ
 →アップロード
・努力
 →チート

最近のトラックバック

ブログ内検索

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。