Threadとtimeoutと後始末

# 閉じられないファイルの例
Thread.start {
  f = open('foo', 'w')
  if false
    raise x
  end
  f.close
}

さて、このままスクリプトが終了するならいいんだけど、これが何度も繰り返されるとファイルがいつまでたっても閉じられないまま困ることになるかもしれない。ファイル以外にもデータベースとかSMTPとか、最後にcloseやdisconnectやfinishが必要なものって結構ある。

そんな時はensureを使えば大丈夫。

Thread.start {
  begin
    f = open('foo', 'w')
    if false
      raise x
    end
  ensure
    # 例外が投げられてもここは必ず実行される。
    f.close if f
  end
}


スレッドが途中で終了させられても大丈夫。

t = Thread.start {
  begin
    sleep 3 # 眠っている間に、
    f = open('foo', 'w')
    if false
      raise x
    end
  ensure
    # こんな時でも必ず実行される。
    f.close if f
  end
}
t.kill # 眠っている間に(ファイルを開く前に)スレッドを止めてしまう

この時はたぶんfがnilなので if f が効いてくる。

スレッド内のメソッドでファイルを開いたりしているときはメソッド内でensureすれば大丈夫。同じように正常終了だろうと例外だろうとスレッドが終わろうと必ずensureは実行されるので。

def foo_method
  f = open('foo', 'w')
  if false
    raise x
  end
ensure
  f.close if f
end

Thread.start {
  foo_method()
}

タイムアウトでも全く同じ

timeout(5) {
  begin
    sleep 10
    puts '10秒寝る前にタイムアウトするから出力されない'
  ensure
    puts 'でもここはどんな時でも出力される'
  end
}

タイムアウトはスレッドで実装されているので。