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状態のスレッドを中断させるメソッド」というものが分からなかったので、その点を省略しています。
なので、「命令を受けてもすぐに終わらず後処理をしよう」という部分は理解できたのですが、サンプルとしてはどうなのかなあと思います。