Tbpgr Blog

Employee Experience Engineer tbpgr(てぃーびー) のブログ

Ruby で Future パターンを実装する

概要

RubyでFutureパターンを実装する

内容

Futureパターンは実行結果を得るまでに時間がかかる処理をする際に
結果の代わりに引換券をもらいます。

例えばドラクエの購入をする際に予約がなければ発売当日にずっと並ばなければなりません。
ここで予約をしておけば予約券を受け取って実際に発売日が来たら受け取ることが可能です。
当日受け取り時間が遅れたとすれば待機することになります。

補足

※補足実際はこの例より簡単に実装可能。
 あくまでパターンの理解を優先してRubyの言語側で
 用意してくれている仕組みは利用せずに処理します。

簡易実装版については以下参照
RubyでFutureパターンを実装する(シンプル編)

UML

plantUMLソース

@startuml{future_pattern.png}
title Future Pattern
class Main
class Host {
  request
}
class FutureData {
  realdata
  getContent()
  setRealData()
}
class RealData {
  content
  getContent()
}
interface Data {
  getContent()
}

Main --> Host : Request
Main --> Data : Uses in future
Host --> FutureData : Creates
FutureData ..|> Data
RealData ..|> Data
FutureData o-- RealData
@enduml

サンプルコード

# encoding: utf-8
require 'thread'

class Host
  def request(id, value)
    future_data = FutureData.new

    Thread.start do
      # 時間のかかる処理を想定
      sleep id
      real_data = RealData.new(id, value);
      future_data.set_real_data(real_data);
    end

    return future_data
  end
end

class RealData
  def initialize(id, value)
    @id, @value = id, value
  end

  def get_content()
    return @id, @value
  end
end

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

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

  def complete?
    @complete
  end

  def complete!
    @complete = true
  end

  def set_real_data(real_data)
    @mutex.synchronize do
      return if complete?

      @real_data = real_data
      complete!
      @cv.broadcast
    end
  end

  def get_content()
    @mutex.synchronize do
      @cv.wait(@mutex) while (!complete?)
      return @real_data.get_content()
    end
  end
end

host = Host.new()
puts Time.now
future1 = host.request(1, 'hoge')
future2 = host.request(2, 'hige')
future3 = host.request(10, 'hage')
puts Time.now

# 時間のかかる処理を想定
sleep(5)

ret1 = future1.get_content()
ret2 = future2.get_content()
ret3 = future3.get_content()
puts Time.now

print ret1
puts
print ret2
puts
print ret3
puts

出力

スレッド内部でidに指定した時間待機が発生していますが
Futureの取得前後では全く処理時間がかかっていないことがわかります。
その後、5秒待機させていますが待機中もスレッドの処理が進行しているため
最終処理時間は15秒にならずに10秒になっています。

スレッドを利用せずに実行するのに比べて5秒節約出来ています。

2013-06-23 01:07:28 +0900
2013-06-23 01:07:28 +0900
2013-06-23 01:07:38 +0900
[1, "hoge"]
[2, "hige"]
[10, "hage"]