Ruby の Monitor

Ruby には Monitor というクラスがあるんだけど、よく見るわりに Mutex との違いをよくわかっていなかった。 たまたま Java Concurrency in Practice を読んでいたら 2.3.2 Reentrancy で同じような概念の話が出てきて、少しわかった気がするのでそのメモ。

Java Concurrency in Practice

Java Concurrency in Practice

  • 作者: Brian Goetz,Tim Peierls,Joshua Bloch,Joseph Bowbeer,David Holmes,Doug Lea
  • 出版社/メーカー: Addison-Wesley Professional
  • 発売日: 2006/05/09
  • メディア: ペーパーバック
  • 購入: 7人 クリック: 14回
  • この商品を含むブログ (22件) を見る

Monitor とは

Ruby の Monitor は何度も lock 出来る Mutex とドキュメントに書いてある。 特になんの意味もない例だけど以下のようにロック中にさらにロックできる(もちろん同じスレッド内で)。 もしこのコードを Mutex を使用して m = Mutex.new とした場合、2回めの m のロックする部分(m.synchronize)でデッドロックが発生するが Monitor を使用すると発生しない。

m = Monitor.new
m.synchronize do
  m.synchronize do
    # your code
  end
end

使い所

たぶん使い所は、すでにあるコードを組みわせてアトミックな処理をするところ。 下のようなコードのような感じ。もともと Nanika クラスに sugoi というスレッドセーフなメソッドがあったとする。 この時、メソッド内でアトミックに sugoi を2回呼び出す ultra_sugoi というメソッドを新たに追加しようとしたときに Monitor が使えそう。 Monitor ではなく Mutex を使用すると、ultra_sugoi 内でロックを取ってから sugoi のメッソド呼び出しが発生して、 sugoi 内で またロックを取りに行きデッドロックが発生する。 しかし、Montior を使用すると何度でもロックが成功するので、 sugoi をアトミックに2回呼び出せる。

class Nanika
  def initialize
    @monitor = Monitor.new
    @sugoi_value = 0
  end

  def sugoi
    @monitor.synchronize do
      @sugoi_value += 1
    end
  end

  def ultra_sugoi
    @monitor.synchronize do
      sugoi
      sugoi
    end
  end
end

nanika = Nanika.new

[*1..2].map {
  Thread.new do
    nanika.ultra_sugoi
  end
}.each(&:join)

その他

javaの本では reentrant という言葉で説明されていた。

reentrancy とは

Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis.

リエントラント性とは、呼び出し単位ではなくスレッド単位でロックが取得される。

Lock with reentrant

同じスレッドであれば、何度でもロックを取得できる ロックしたスレッドとそのスレッド内でのロック保持数を記録しておく。Ruby の Monitor。

Lock without reentrant

呼び出し(処理)単位でロックを取っているので、同じスレッドであっても再度ロックを取れない。 単純にロックされているかどうかだけを見る。Ruby の Mutex。

RubyはGILが

そうね