dak ブログ

python、rubyなどのプログラミング、MySQL、サーバーの設定などの備忘録。レゴの写真も。

rubyでflockを使った排他制御(その2)

2011-04-19 00:17:44 | ruby
先日の『rubyでflockを使った排他制御』では、排他されているプロセスはずっと待ち状態になってしまいます。
そのため、いくつかのプロセスが常に動作している環境では、ずっと待ち状態になってしまう可能性があります。

ブロックされている場合の処理を記述したい場合には、flock の引数に File::LOCK_NB を指定します。

■プログラム例
#!/usr/local/bin/ruby
#
# flock による排他制御
# usage: flock_nb.rb {ID} {retry} {interval}
#

$KCODE = 'u'
require 'jcode'
require 'time'

# lock
# @param lockfile
# @param num_retry リトライ回数
# @param interval リトライ間隔
# @return lockfileのstream / nil
def lock(lockfile, num_retry, interval)
st = File.open(lockfile, 'a')
if ! st
STDERR.print("failed to open\n")
return nil
end

0.upto(num_retry) do |i|
begin
res = st.flock(File::LOCK_EX | File::LOCK_NB)
return st if res

print("[#{Time.now().to_s}] sleep [#{ARGV[0]}]\n")
sleep(interval)
rescue
STDERR.print("failed to lock\n")
return nil
end
end

return nil
end

# unlock
# @param lockfile
# @return true / false
def unlock(st)
begin
st.flock(File::LOCK_UN)
st.close
rescue
STDERR.print("failed to unlock")
return false
end

return true
end

sleep(5)

lock_st = lock('lockfile', ARGV[1].to_f, ARGV[2].to_f)
if ! lock_st
print("[#{Time.now().to_s}] failed to lock [#{ARGV[0]}]\n")
exit
end

print("[#{Time.now().to_s}] get lock [#{ARGV[0]}]\n")
sleep(10)
print("[#{Time.now().to_s}] release lock [#{ARGV[0]}]\n")

unlock(lock_st)

■実行結果
$ ./flock_nb.rb 1 3 3 &
$ ./flock_nb.rb 2 3 3 &
$ ./flock_nb.rb 3 3 3 &
[Tue Apr 19 00:15:43 +0900 2011] get lock [1]
[Tue Apr 19 00:15:46 +0900 2011] sleep [2]
[Tue Apr 19 00:15:48 +0900 2011] sleep [3]
[Tue Apr 19 00:15:49 +0900 2011] sleep [2]
[Tue Apr 19 00:15:51 +0900 2011] sleep [3]
[Tue Apr 19 00:15:52 +0900 2011] sleep [2]
[Tue Apr 19 00:15:53 +0900 2011] release lock [1]
[Tue Apr 19 00:15:54 +0900 2011] get lock [3]
[Tue Apr 19 00:15:55 +0900 2011] sleep [2]
[Tue Apr 19 00:15:58 +0900 2011] failed to lock [2]
[Tue Apr 19 00:16:04 +0900 2011] release lock [3]