MonitorMixin

MonitorMixin

동시성 프로그래밍에서 모니터(monitor)는 여러 스레드가 안전하게 사용하도록 설계된 객체나 모듈이에요. 모니터의 핵심 특징은 그 메서드들이 상호 배제(mutual exclusion)로 실행된다는 점이에요. 즉 어느 시점에든 단 하나의 스레드만 그 메서드 중 하나를 실행할 수 있죠. 이 상호 배제 덕분에, 데이터 구조를 갱신하는 병렬 코드를 추론하는 것보다 모니터 구현을 추론하는 게 훨씬 단순해져요. 일반 원리는 Wikipedia의 Monitor 문서에서 더 읽을 수 있어요.

출처: Ruby 4.0 API

본문

MonitorMixin을 객체에 extend하거나 클래스에 include해서 사용해요. 각각의 사용 예시를 볼게요.

객체에 extend하기

require 'monitor'

buf = []
buf.extend(MonitorMixin)
empty_cond = buf.new_cond

# consumer
Thread.start do
  loop do
    buf.synchronize do
      empty_cond.wait_while { buf.empty? }
      print buf.shift
    end
  end
end

# producer
while line = ARGF.gets
  buf.synchronize do
    buf.push(line)
    empty_cond.signal
  end
end

소비자 스레드는 buf.empty?인 동안 생산자 스레드가 buf에 줄을 밀어 넣기를 기다려요. 생산자 스레드(메인 스레드)는 ARGF에서 한 줄을 읽어 buf에 넣고, empty_cond.signal을 호출해 새 데이터가 있음을 소비자 스레드에 알려줘요.

클래스에 include하기

require 'monitor'

class SynchronizedArray < Array
  include MonitorMixin

  def initialize(*args)
    super(*args)
  end

  alias :old_shift :shift
  alias :old_unshift :unshift

  def shift(n=1)
    self.synchronize do
      self.old_shift(n)
    end
  end

  def unshift(item)
    self.synchronize do
      self.old_unshift(item)
    end
  end

  # other methods ...
end

SynchronizedArray는 항목 접근을 동기화하는 Array예요. Array의 서브클래스로 만들고 MonitorMixin 모듈을 include한 형태죠.

Public Class Methods

new (...)

이 생성자 대신 extend MonitorMixin이나 include MonitorMixin을 사용하세요. 사용법은 위 예시를 참고해요.

Public Instance Methods

mon_enter ()

임계 구역에 진입해요.

mon_exit ()

임계 구역에서 나와요.

mon_locked? ()

이 모니터가 어떤 스레드에 의해 잠겨 있으면 true를 돌려줘요.

mon_owned? ()

이 모니터가 현재 스레드에 의해 잠겨 있으면 true를 돌려줘요.

mon_synchronize (&b)

임계 구역에 들어가 블록을 실행해요. 블록이 끝나면 자동으로 임계 구역에서 나와요. synchronize로도 부를 수 있어요.

mon_try_enter ()

임계 구역 진입을 시도해요. 락을 얻지 못하면 false를 돌려줘요. try_mon_enter로도 부를 수 있어요.

new_cond ()

Monitor 객체와 연결된 새 MonitorMixin::ConditionVariable을 만들어요.

synchronize (&b)

mon_synchronize의 별칭이에요.

try_mon_enter ()

이전 버전과의 호환성용이에요. mon_try_enter의 별칭이에요.

mon_check_owner ()

MonitorMixin이 현재 스레드에 소유돼 있는지 확인하고, 아니면 예외를 던져요.

mon_initialize ()

클래스에 include하거나 객체에 extend한 뒤 MonitorMixin을 초기화해요.