それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

Rubyでマルチスレッド その8 # Read-Write Lock

Ruby結城浩せんせいのJavaマルチスレッド本 8 # Read-Write Lock

読込中は書かない。

ログファイルのFlushとか、MySQLだとSELECT for UPDATE ってところか。


rubyで書いてみた

#!/usr/bin/env ruby
require 'thread'
# Read Write Lock

class ReadWriteLock
  def initialize
    @reading_readers = 0
    @waiting_writers = 0
    @writing_writers = 0
    @prefere_writers = true
    @m = Mutex.new
    @cv = ConditionVariable.new
  end
  def read_lock
    @m.synchronize {
    while( @writing_writers > 0 || (@prefere_writers && @waiting_writers > 0) ) do
      @cv.wait(@m)
    end
    @reading_readers = @reading_readers + 1
    }
  end
  def read_unlock
    @m.synchronize {
      @reading_readers = @reading_readers - 1
      @prefere_writers = true
      @cv.broadcast
    }
  end
  def write_lock
    @m.synchronize{
      @waiting_writers = @waiting_writers + 1
      begin
        while( @reading_readers > 0 && @waiting_writers > 0 ) do
          @cv.wait(@m)
        end
      ensure
        @waiting_writers = @waiting_writers - 1
      end
      @waiting_writers = @waiting_writers + 1
    }
  end
  def write_unlock
    @m.synchronize {
      @waiting_writers = @waiting_writers - 1
      @prefere_writers = true
      @cv.broadcast
    }
  end
end

class DataClass
  def initialize
    @lock = ReadWriteLock.new
    @buffer = (1..10).map{"*"}.join
  end
  def read 
    @lock.read_lock
    begin
      c= self.doRead
    ensure
      @lock.read_unlock
    end
    c
  end
  def write c
    @lock.write_lock
    begin
      self.doWrite c
    ensure
      @lock.write_unlock
    end
  end
  def doRead
    str = @buffer
    self.slowly
    str
  end
  def doWrite c
    @buffer = (1..10).map{ c }.join
    self.slowly
  end
  def slowly
    sleep 50.to_f/1000
  end
end
class WriterThread < Thread
  def initialize data, filter
    @data = data
    @filter = filter
    @index = 0
    super{
      while true do 
        c = self.next_char.chr
        @data.write c
        sleep rand 2
      end
    }
  end
  def next_char
    c = @filter[@index]
    @index = @index + 1 if( @index <  @filter.length )
    @index = 0          if( @index >= @filter.length )
    return c
  end
end

class ReaderThread < Thread
  def initialize data
    @data = data
    super{
      while true do
        c = @data.read
        puts self.to_s + " read #{c}"
      end
    }
  end
end

data = DataClass.new
threads = [
  ReaderThread.new(data),
  ReaderThread.new(data),
  ReaderThread.new(data),
  ReaderThread.new(data),
  ReaderThread.new(data),
  WriterThread.new(data ,("A".."Z").map.join), # 
  WriterThread.new(data ,("a".."z").map.join), #
]
threads.each{|t| t.join}

puts "End"

ロックの衝突

スレッド1 スレッド2
read read 並行
write read 衝突
write write 衝突

ruby添付ライブラリ

添付ライブラリにWrite/Readのロックを提供するパッケージがあるようだ。試してない。