Two-Phase Termination
マルチスレッドパターン続き。
終了要求をうけ終了する前に、「終了状態」を挟むことによって安全性、生存性、応答性を確保しようというパターン。
今回はjavaのThread#interruptにあたるrubyのメソッドが見あたらなかったので、本とは少し異なったイメージになっています。
サンプルプログラム:ロガー
サンプルとして乱数をログ出力し続けるスクリプトを作ります。
これは0〜9の数字から乱数をとり、ログ出力するもので、フォーマットは
<<[タイムスタンプ]>> [生成した乱数] --------------
にしたいとします。
また、乱数アウトプットと罫線を引く部分の間にはsleepを入れています。重い処理の代わりです。
終了状態のないロガー
stop処理として、Thread#killを呼び出してスレッドを止めています。
require 'thread' require 'monitor' class Logger include MonitorMixin def initialize @t = nil super() end def start synchronize do @t = Thread.start do loop do File.open(log, 'a') do |w| w.puts "<<#{Time.now}>>" for i in 1..9 do w.print rand(i) end w.print "\n" sleep(1) #重い処理 w.puts "--------------" end end end end end def stop @t.kill end end l = Logger.new l.start sleep(3) l.stop
これで出力される結果。
<<Sat Feb 09 10:54:50 +0900 2008>> 002033150 -------------- <<Sat Feb 09 10:54:51 +0900 2008>> 002034275 -------------- <<Sat Feb 09 10:54:52 +0900 2008>> 002131448
最後の罫線が出力されていません。
ここから、stopはThread#killを呼び出してその場で終了してしまうため、処理が中途半端で止まってしまったことがわかります。
終了状態のあるロガー
@finishedを介することで、stopが呼ばれた後も終了状態として動くようにしたロガーです。
require 'thread' require 'monitor' class Logger include MonitorMixin def initialize @t = nil @started = false @finished = false super() end def start synchronize do return if @started || @finished @t = Thread.start do until @finished do File.open(log, 'a') do |w| w.puts "<<#{Time.now}>>" for i in 1..9 do w.print rand(i) end w.print "\n" sleep(1) #重い処理 w.puts "--------------" end end end @started = true end end def stop return unless @started return if @finished @finished = true @t.join end end l = Logger.new l.start sleep(3) l.stop
実行結果。
<<Sat Feb 09 11:03:00 +0900 2008>> 011110127 -------------- <<Sat Feb 09 11:03:01 +0900 2008>> 002040324 -------------- <<Sat Feb 09 11:03:02 +0900 2008>> 012233203 --------------
今度は罫線が表示されています。
stop呼び出しですぐには終了していないからです。
今回は
javaにおけるThread#interruptのような「wait/sleep状態のスレッドを中断させるメソッド」というものが分からなかったので、その点を省略しています。
なので、「命令を受けてもすぐに終わらず後処理をしよう」という部分は理解できたのですが、サンプルとしてはどうなのかなあと思います。