RubyでSingletonパターン考察1

スポンサーリンク
スポンサーリンク
ライフスタイル関連のコンテンツ
お金 | 仕事 | 勉強 | プライベート | 健康 |
プログラミング関連のコンテンツ
C言語/C++入門 | Ruby入門 | Python入門 | プログラミング全般

Rubyでデザインパターンの一つである、Singletonパターンを考えてみる。

スポンサーリンク

単純なSingletonパターン

class Singleton
  @@singleton = nil
 
  # new をprivateにする
  private_class_method(:new)
 
  def self.singleton
    if @@singleton == nil
      @@singleton = new
    end
    return @@singleton
  end
end
 
obj1 = Singleton.singleton
obj2 = Singleton.singleton
obj3 = Singleton.singleton
puts obj1.object_id
puts obj2.object_id
puts obj3.object_id

実行結果。

21102270
21102270
21102270

マルチスレッド実行での落とし穴がある

すべて3つともobject_idが等しく、Singletonが正しく動作しているように見えるが、一つだけ穴がありまして・・・
このコードは、マルチスレッドの場合に、複数のインスタンスが生成されてしまう可能性があります。
それは、以下の部分、

  def self.singleton
    if @@singleton == nil
      @@singleton = new
    end
    return @@singleton
  end

の、if 条件式と、newにより@@singletonにインスタンスが代入される間の部分。
現在のスレッドが@@singletonへのインスタンス代入を行おうとする直前に、スレッドが切り替わり、別のスレッドでのif 条件式の判定が行われた場合、複数のインスタンスが生成されてしまう可能性がある。

マルチスレッド実行時に複数インスタンスが生成されてしまう例

分かりやすいように、上記のif 条件式の後にsleepをはさんで、マルチスレッドで実行してみます。

class Singleton
  @@singleton = nil
 
  # new をprivateにする
  private_class_method(:new)
 
  def self.singleton
    if @@singleton == nil
      sleep 3
      @@singleton = new
    end
    return @@singleton
  end
end
 
puts "--- マルチスレッドの場合 ---"
threads = []
for i in 1..3
  t = Thread.new do
    obj =  Singleton.singleton
    puts "#{obj.object_id}\n"
  end
  threads << t
end
threads.each do |th|
  th.join
end

実行結果。

--- マルチスレッドの場合 ---
21101870
21101830
21101790

object_idが変わってしまい、インスタンスが複数生成されてしまっています。
これでは、Singletonパターンになりません。
次回に、どう書けば、マルチスレッド対応のSingletonパターンになるか考察。

スポンサーリンク
 
スポンサーリンク