2010年12月1日水曜日

マルチスレッド デザインパターン(Future パターン) Ruby編


マルチスレッドデザインパターンについてまとめる。
今回はFutureパターン。


Futureパターンとは、実行結果の代わりに引換券をもらい、
のちのち(未来)に、その引換券を使って結果を得るスレッドパターンである。

例えば、ケーキ屋でケーキを注文し、引換券をもらう。
夕方、再度ケーキ屋に足を運び、引換券とケーキを交換してもらう。
そういった場合に利用する。


#!/usr/bin/ruby
require 'thread'
Thread.abort_on_exception = "yes"

class Host
  # requestメソッド内で利用される引数やローカル変数は
  # このrequestメソッドを呼び出したスレッドに固有である
  # 複数のスレッドで共有されないためsynchronize処理は不要である
  def request(request_id)
    # FutureDataのインスタンスを処理結果を待たないで
    # returnするため、スレッド外で事前に作成しておく
    future_data = FutureData.new()

    Thread.start do
      real_data = RealData.new();
      real_data.handle(request_id);

      future_data.setRealData(real_data);
     end

    puts("Request Post:" + request_id.to_s)

    return future_data
  end
end


class RealData
  def initialize()
    @result = nil
  end

  def handle(request_id)
    puts("Request handling:" + request_id.to_s)
    sleep(rand(5))
    @result = "Work End:" + request_id.to_s
  end

  def getContent()
    return @result
  end
end


 class FutureData
  def initialize()
    @is_ready = false
    @real_data = nil

    @mutex = Mutex.new
    @cv = ConditionVariable.new
  end

  def setRealData(real_data)
    @mutex.synchronize do
      # balkingパターン
      # 2回以上setRealDataが呼ばれないようにする
      if (@is_ready == true)
        return
      end

      @real_data = real_data
      @is_ready = true

      # FutureDataインスタンスごとに状態変数は固有である
      # よって再開処理はそのインスタンスのみに適用される
      # 状態変数を待っているスレッドがすべて再開されるわけではない
      @cv.broadcast
    end
  end

  def getContent()
    @mutex.synchronize do
      while (@is_ready == false)
        @cv.wait(@mutex)
      end
      return @real_data.getContent()
    end
  end
end

host = Host.new()
data1 = host.request(1)
data2 = host.request(2)
data3 = host.request(3)

t1 = Thread.start do
  puts(data1.getContent())
end

t2 = Thread.start do
  puts(data2.getContent())
end

t3 = Thread.start do
  puts(data3.getContent())
end

t1.join
t2.join
t3.join


処理結果は下のようになる。
$ ruby future.rb
Request handling:1
Request Post:1
Request handling:2
Request Post:2
Request handling:3
Request Post:3
Work End:2
Work End:3
Work End:1


実際にFutureパターンを利用した(Active Objectパターン内でFutureパターンを利用している)をあげておく。

また、スレッドのデザインパターン以前の並行処理プログラミングの基礎はここを参考してほしい。