ラベル design_pattern の投稿を表示しています。 すべての投稿を表示
ラベル design_pattern の投稿を表示しています。 すべての投稿を表示

2013年12月15日日曜日

C言語でポリモーフィズム(polymorphism)を実現

C言語にはオブジェクト指向言語のようにオブジェクトに
メソッド(手続き)を持ち運ぶ手段は提供されていない。

ただし、関数ポインタを高階関数として利用すれば似たようなことはできる。
オブジェクト指向言語ではないので、ダブルディスパッチを用いて
目的を達すると言ったほうが適切かもしれない。

デザインパターンであるvisitor(ビジター)パターンは
オブジェクト指向言語でしばしば利用される。
しかしこれは決してJavaやC++だけで利用できるパターンではない。
これを非オブジェクト指向言語であるCで実装してみる。


visitor パターンとは
(wikipediaより)
オブジェクト指向プログラミング およびソフトウェア工学 において、
アルゴリズムをオブジェクトの構造から分離するためのデザインパターンである。
分離による実用的な結果として、既存のオブジェクトに対する新たな操作を
構造を変更せずに追加することができる。

さて、パターンの定義はややこしいが、意味と役割は非常に分かりやすい。
難しく考える必要はない。一言でこのパターンの利用用途を表すと下記になる。

visitする関数、visitされる関数がどちらも部品として取り替える必要がある場合に使う。
- visitorする側は、visit()メソッドを実装する。
- visitされる側は、accept(visitor)メソッドを実装する。


関数ポインタが不慣れな場合もあるだろう。
基礎から復習を始める。

◆ 関数ポインタについて
// int型を引数に取るvoid型を返す(何も返さない)、関数へのポインタを宣言
void (*fp)(int); // void (*fp)(); でよい

// 適当な関数を作成
void my_func(int n) {
printf("%d\n", n);
}

// 関数の先頭アドレスをポインタへ代入
fp = my_func; // &my_funcでもよい

// 呼び出し
(*fp)(10);



復習を終えたところでvisitorパターンのコードを書いてみる。

◆ サンプル例
#include <stdio.h>

void (*visit)();
void (*accept)(void (*visit)()); // void (*accept)();でよい


void accept1(void (*visit)()) {
puts("accept1");
(*visit)();
}

void accept2(void (*visit)()) {
puts("accept2");
(*visit)();
}

void visit1() {
puts("visit1");
}

void visit2() {
puts("visit2");
}


int main() {
accept = accept1;
visit = visit1;
accept(visit);

accept = accept2;
visit = visit2;
accept(visit);

return 0;
}


結果はこうなる。
accept1
visit1
accept2
visit2



関数ポインタのプロトタイプ宣言(typedef)をしておけば
可読性があがるだろうか。

◆ サンプル例(typedef版)
#include <stdio.h>

// 関数ポインタをtypedefで定義
typedef void (*visit_type)();

typedef void (*accept_type)(visit_type); // typedef void (*accept_type)();でよい


void accept1(visit_type visit) {
puts("accept1");
visit();
}

void accept2(visit_type visit) {
puts("accept2");
visit();
}

void visit1() {
puts("visit1");
}

void visit2() {
puts("visit2");
}


int main() {
// 型の変数を宣言
visit_type visit;
accept_type accept;

accept = accept1;
visit = visit1;
accept(visit);

accept = accept2;
visit = visit2;
accept(visit);

return 0;
}

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パターンを利用している)をあげておく。

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



2010年11月23日火曜日

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

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


【利用用途】
activeとは能動的な、という意味である。
本パターンは、外部から非同期にメッセージを受け取っても、
自分固有のスレッドで、自分の都合のいいタイミングで処理を実行させたい場合に利用する。
非同期にリクエストを飛ばすクライアントと、それを処理するサーバを想像してもらえると分かりやすい。



【クラス役割】
クライアント側 クラス一覧
CalcClient
 計算をサーバに依頼する。
 計算は時間がかかるという前提で、Futureパターンを利用する。

PrintClient
 出力をサーバに依頼する。


◆ サーバ側 クラス一覧
Proxy
 クライアントからの処理を受け付ける窓口の役割を担う。
 また、スケジューラへのリクエストの登録を行う。
 本クラスの仕事は、メソッドの呼び出しをインスタンスに変換することである。
 それぞれのリクエストが、リクエストクラスを元に作られるインスタンスで
 管理されるということである。
 プロキシの名前から分かるとおり、実処理は行わない。
 プロキシを介するということは、インターフェース(API)を変えることなく、
 実装の変更もできる。
 起動(invocation)と実行(execution)の分離、というやつである。

Scheduler
 キューへの出し入れをスケジューリングする。
 プロキシからリクエストを渡され、キューに投入する。
 また、キューからリクエストを取得し、リクエストの実行を依頼する。
 スケジューリング処理は単純なFIFO(First In First Out)である。

ActivationQueue
 リクエストを一定の数だけ保持する。
 キューへのリクエストの出し入れはスケジューラが行う。

Request
 クライアントから依頼されたリクエストを管理する。
 ここでは実処理を行うインスタンスと、Futureパターンで利用するインスタンスを保持させる。

CalcRequest(Requestクラスの継承クラス)
 計算処理を実担当クラスへ依頼する。

PrintRequest(Requestクラスの継承クラス)
 出力処理を実担当クラスへ依頼する。

Servant
 実処理を担う。
 今回は、計算と出力を行うためのメソッドを保持している。

FutureResult
 futureパターン用の引換券クラスである。

RealResult
 futureパターン用の実データクラスである。

ActiveObjectFactory
 サーバ側の処理に必要なインスタンスを作成する。
 つまり初期化である。



【処理フロー】
◆ クライアント側 フロー
 サーバのプロキシへ処理を依頼 →
 クライアントは引換券を取得 
 その引換券を元に結果を取得

◆ サーバ側 フロー
 プロキシがリクエストを引き受け、スケジューラへ管理を依頼 
 スケジューラはキューへリクエストを投入 
 スケジューラは必要に応じてキューからリクエストを取得 
 リクエストを管理するインスタンスが実処理担当を担うインスタンスへ処理を依頼 
 リクエストを管理するインスタンスは、結果をフューチャ用インスタンスへセット
 ※プログラム上は、結果は実結果クラスとして管理している




【コード】
#!/usr/bin/ruby

require 'thread'

Thread.abort_on_exception = "yes"

##### CLIENT #####
class CalcClient
  def initialize(proxy, num)
    @proxy = proxy
    @num = num
  end

  def start()
    future_result = @proxy.calc(@num)
    @real_result = future_result.getResult()
    puts("CalcResult:" + @real_result.getRealValue().to_s)
  end
end


class PrintStrClient
  def initialize(proxy, str)
    @proxy = proxy
    @str = str
  end

  def start()
    @proxy.printStr(@str)
  end
end


##### SERVER #####
class Proxy
  def initialize(scheduler, servant)
    @scheduler = scheduler
    @servant = servant
  end

  # 実処理を行うインスタンスのメソッド名と同一である
  def calc(num)
    future = FutureResult.new()
    @scheduler.invoke(CalcRequest.new(@servant, future, num))
    return future
  end

  # こちらは引き換え券は不要なため、スケジューラに処理を移譲するのみである
  def printStr(str)
    @scheduler.invoke(PrintRequest.new(@servant, str))
  end
end


class Scheduler
  def initialize(queue)
    @queue = queue
  end

  def invoke(request)
    @queue.putRequest(request)
  end

  def start()
    Thread.start do
      # サーバとして稼動させると想定したのでループさせておけばよいとした
      # ただし本コードではクライアントスレッドが終了した時点で
      # 終了する(joinさせていないため)
      loop do
        request = @queue.takeRequest()
        request.execute()
      end
    end
  end
end


class ActivationQueue
  def initialize(max_request_size)
    @queue = Array.new
    @max_request_size = max_request_size
    @mutex = Mutex.new
    @cv = ConditionVariable.new
  end

  def putRequest(request)
    @mutex.synchronize do
      while  @queue.size >= @max_request_size
        @cv.wait(@mutex)
      end
    
      @queue.push(request)
      @cv.broadcast
    end
   end

  def takeRequest()
    @mutex.synchronize do
      while @queue.size() == 0
        @cv.wait(@mutex)
      end
      request = @queue.shift
      @cv.broadcast
      return request
    end
  end
end


class Request
  def initialize(servant, future)
    @servant = servant
    @future = future
  end
end


class CalcRequest < Request
  def initialize(servant, future, num)
    super(servant, future)
    @num = num
  end

  def execute()
    real_result = @servant.calc(@num)
    @future.setResult(real_result)
  end
end


class PrintRequest < Request
  def initialize(servant, str)
    # 結果を得る必要はないので、引換券はnilにする
    super(servant, nil)
    @str = str
  end

  def execute()
    @servant.printStr(@str)
  end
end


class Servant
  def calc(num)
    answer = num * 10
    sleep 1
    return RealResult.new(answer)
  end

  def printStr(str)
    puts("PrintStr Result:" + str.to_s)
  end
end


class FutureResult
  def initialize()
    @mutex = Mutex.new
    @cv = ConditionVariable.new
    @is_ready = false
  end

  def setResult(real_result)
    @mutex.synchronize do
      @real_result = real_result
      @is_ready = true
      @cv.broadcast
    end
  end

  def getResult()
    @mutex.synchronize do
      while !@is_ready
        @cv.wait(@mutex)
      end
    end

    return @real_result
  end
end


class RealResult
  def initialize(value)
    @value = value
  end

  def getRealValue()
    return @value
  end
end


class ActiveObjectFactory
  def createActiveObject()
    # リクエストをキューイングするインスタンスの作成
    queue = ActivationQueue.new(10)

    # キューへの出し入れをスケジューリングするインスタンスの作成
    scheduler = Scheduler.new(queue)

    # 実処理を担うインスタンスの作成
    servant = Servant.new()

    # クライアントからの処理を受け付ける窓口の役割を担うインスタンスの作成
    proxy = Proxy.new(scheduler, servant)
    scheduler.start()
    return proxy
  end
end


################################
### サーバ側の処理に必要なインスタンスの作成
active_object_factory = ActiveObjectFactory.new()
proxy = active_object_factory.createActiveObject();


################################
### クライアント側の処理に必要なインスタンスの作成
threads = Array.new

threads << Thread.start do
  calc_client = CalcClient.new(proxy, 1)
  calc_client.start()
end

threads << Thread.start do
  print_str_client = PrintStrClient.new(proxy, 1)
  print_str_client.start()
end
 
threads << Thread.start do
  calc_client = CalcClient.new(proxy, 2)
  calc_client.start()

threads.each do |t|
  t.join
end

処理結果は下のようになる。
$ ./activeobject.rb
Calc     Result:10
PrintStr Result:1
Calc     Result:20

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



2010年10月25日月曜日

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



マルチスレッドデザインパターンであるProducer-Consumerパターンについてまとめる。
このパターンはスレッド間で処理の待ち合わせを行いながら処理を実行させていきたい場合に利用する。


【登場人物】
・キューにデータを入れるスレッド
・キューからデータを取り出すスレッド

【条件】
・キューが空であるならば、データの取得処理は待たなければならない
・キューのサイズの上限まできたならば、データの入れ込み処理は待たなければならない

なぜキューを使うかというと、ProducerとConsumerの処理スピードが違うとスループットが落ちるためであるそこで中継地点としてキューをおき、そこにデータを保持させ、処理スピードの違いを吸収させる。

今回はRubyを利用する。
Javaであればwait、notifyAllを使う場面ではそれぞれ、ConditionVariableのwait、broadcastを利用している。また、今回は待機させる全スレッドを再稼働させるためsignal(notify)ではなく、broadcastを使う方針をとる。
(参考)
http://www.techscore.com/tech/Java/JavaSE/Thread/5-2/



#!/usr/bin/ruby

require 'thread'

class Table
  def initialize()
    @queues = Array.new
    @queue_max = 5

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

  def push(num)
    @mutex.synchronize do
      while @queues.size() >= @queue_max
        @cv.wait(@mutex)
      end

      @queues.push(num)

      @cv.broadcast
    end
  end

  def take()
    @mutex.synchronize do
      while @queues.size() == 0
        @cv.wait(@mutex)
      end

      queue = @queues.shift()

      @cv.broadcast
    end
  end
end


class Push_Take
  def initialize(table)
    @table = table
  end

  def start()
    @push_thread = Thread.start do
      10.times do |num|
        @table.push(num)
        puts "push"
      end
    end

    @take_thread = Thread.start do
      10.times do
        @table.take()
        puts "take"
        sleep 0.5
      end
    end

    @push_thread.join
    @take_thread.join
  end
end


table = Table.new
push_take = Push_Take.new(table)
push_take.start()

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