それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

Rubyでマルチスレッド 14 #ActiveObject

Ruby結城浩せんせいのJavaマルチスレッド本 その14 # activeobject

rubyで書いた。


長いので折りたたみ

#!/usr/bin/env ruby
module ActiveObject
  require "thread"
  class MethodRequest
    def initialize( servant,future )
      @servant = servant
      @future = future
    end
    def execute() nil end
  end
  class DisplayStringRequest < MethodRequest
    def initialize( servant, str)
      super(servant,nil)
      @content = str
    end
    def execute()
      @servant.display_string @content
    end
  end
  class MakeStringRequest < MethodRequest
    def initialize( servant,future,count,fill_char )
      super(servant, future)
      @count = count
      @fill_char = fill_char
    end
    def execute()
      result = @servant.make_string @count, @fill_char
      @future.set_result result
    end
  end
  class Proxy
    def scheduler() @scheduler end
    def initialize scheduler, servant
      @scheduler = scheduler
      @servant = servant
    end
    def make_string count, fill_char
      future = FutureResult.new
      req =  MakeStringRequest.new(@servant,future,count,fill_char)
      @scheduler.invoke(req)
      return future
    end
    def display_string str
      req = DisplayStringRequest.new(@servant,str)
      @scheduler.invoke(req )
    end
  end
  class Result
    def get_value() nil end
  end
  class RealResult < Result
    def initialize value
      @result_value = value
    end
    def get_value
      @result_value
    end
  end
  class FutureResult < Result
    def initialize
      @m = Mutex.new
      @cv = ConditionVariable.new
      @ready = false
    end
    def set_result(ret)
      @m.synchronize{
        return if(@ready)
        @result = ret
        @ready = true
        @cv.broadcast
      }
    end
    def get_value()
      @m.synchronize{
        #return "準備中" if !@ready
        while(!@ready) do @cv.wait(@m) end
        @result.get_value
      }
    end
  end
  class SchedulerThread
    def get_thread() @t end
    def initialize queue
      @q = queue
    end
    def start
      b=Proc.new{
        loop do
          req = @q.take_request
          sleep 0.05
          req.execute
        end
      }
      @t=Thread.new(&b)
    end
    def invoke( request )
      @q.put_request request
    end
  end
  class ActivationQueue
    MAX_METHOD_REQUEST = 100
    def initialize 
      @list = Array.new
      @size = MAX_METHOD_REQUEST
      @m = Mutex.new
      @cv_full  = ConditionVariable.new
      @cv_empty = ConditionVariable.new
    end
    def put_request req
      @m.synchronize{
        while( @size == @list.size) do
          @cv_full.wait(@m)
        end
        @list.push req
        @cv_empty.broadcast
      }
    end
    def take_request
      @m.synchronize {
        while(@list.empty?) do
          @cv_empty.wait(@m)
        end
        req = @list.shift
        @cv_full.broadcast
        req
      }
    end
  end
  class Servant
    def make_string count, c
      buff= (1..count).map{sleep 10.to_f/1000; c }.join
      result = RealResult.new buff
    end
    def display_string str
      puts "display_string: #{str}"
      sleep 10.to_f/1000
    end
  end
  class ActiveObjectFactory
    def ActiveObjectFactory.create_active_object
      servant = Servant.new
      queue = ActivationQueue.new
      scheduler = SchedulerThread.new queue
      proxy = Proxy.new scheduler, servant
      t=scheduler.start
      return proxy
    end
  end

end


include ActiveObject

class DisplayClientThread
  def initialize name, activeobject 
    @activeobject = activeobject
    @name = name
    @fill_char = name[0].chr
  end
  def start
    t = Thread.new {
        i=0
        while true do
        #戻り値のない呼び出し
        str = "#{@name} #{i}"
        @activeobject.display_string str
        sleep 0.3
        i=i+1
       end
     }
  end
end

class MakerClientThread
  def initialize name, activeobject 
    @activeobject = activeobject
    @name = name
    @fill_char = name[0].chr
  end
  def start
    t = Thread.new {
      i=0
      while true do
        #戻り値のある呼び出し
        result = @activeobject.make_string i, @fill_char
        sleep rand(1000).to_f/1000
        i = i+ 1
        val = result.get_value
        puts "#{@name}: value = #{val}"
       end
     }
  end
end

activeobject = ActiveObjectFactory.create_active_object
threads=[
  activeobject.scheduler.get_thread,
  MakerClientThread.new("Alice", activeobject).start(),
  MakerClientThread.new("Bobby", activeobject).start(),
  DisplayClientThread.new("Chris", activeobject).start(),
]
threads.first.join

実行結果

$ > ruby thread14.rb
display_string: Chris 0
display_string: Chris 1
Bobby: value =
Alice: value =
Alice: value = A
display_string: Chris 2
Bobby: value = B
display_string: Chris 3
Bobby: value = BB
display_string: Chris 4
Bobby: value = BBB
Alice: value = AA
display_string: Chris 5