概要
Read-Write Lockパターン
詳細
スレッドの処理に置いて読むことと書くことの意味は異なります。
・読み込み中に他のスレッドが読み込みをしても問題ない
・読み込み中に他のスレッドが書き込みすると本来読み込みたい内容と異なるため問題がある
・書き込み中は他のスレッドは書き込み不可
排他制御はパフォーマンスを落とすが、読み込み中-読み込み中時をロックを対象外にすることで
パフォーマンスを上げることが出来ます。
構成
Reader:読み取り
Writer:書き込み
SharedResource:読み書き双方で利用している共有リソース。読み取り用と書き込み用のメソッドを持つ
ReadWriteLock:読み書きロック管理用クラス。読み書き別々にロック状態を保持する。
サンプル
仕様
あるデータベースを想定します。
あるテーブルの読み込み中に別のスレッドがテーブルを読み込んでも問題有りません。
あるテーブルの読み込み中に別のスレッドがテーブルを書き込んだ場合で、
そのテーブルが更新処理用に読み込んだ場合、本来の更新意図と異なってしまうため問題があります。
あるテーブルの書き込み中に他スレッドから書き込みされることは問題があります。
※単純化のためにレコードではなくテーブル単位で考えます
構成
Reader:DbReader
Writer:DbWriter
SharedResource:DataBase
ReadWriteLock:Rubyの標準ライブラリSync_m
※自作で作成した場合は、ReadLockは読み込み開始時に書き込み中の処理がなければロックを取得。
ロックを取得出来るまで待機します。ロック取得時は読み込み中のスレッド数をインクリメントします。
読み込みが終了したらアンロック処理を行い、読み込み中スレッド数をデクリメントして、スレッドに通知を行います。
WriteLockは読み書き中のスレッドが1件もなければロックを取得。
ロックを取得できるまで待機します。ロック取得時は書き込み中のスレッド数をインクリメントします。
書き込みが終了したらアンロック処理を行い、書き込み中スレッド数をデクリメントして、スレッドに通知を行います。
ソースコード
# encoding: utf-8 require "pp" require "thread" require "sync" def random_sleep sleep ((rand 10) + 1) * 0.05 end class Table include Sync_m def initialize super @contents = "" end def read(name, count) sync_synchronize(:SH) { sleep 0.2 puts "read:#{name}:#{count}:#{@contents}" return @contents } end def write(value, count) sync_synchronize(:EX) { sleep 0.1 puts "write:#{count}:#{value}" @contents += value } end end class DbReader < Thread def initialize(name, table) block = lambda { (1..5).each do |i| random_sleep ret = "#{table.read name, i}" end } super(&block) end end class DbWriter < Thread def initialize(name, table) block = lambda { (1..5).each do |i| sleep 0.01 table.write("(#{name}:#{i}),", i) end } super(&block) end end table = Table.new threads = [] threads << DbReader.new("雪だるま", table) threads << DbWriter.new("もぐら", table) threads << DbReader.new("ロブスター", table) threads << DbWriter.new("操り人形",table) threads << DbReader.new("トカゲ", table) threads << DbWriter.new("人魚", table) threads << DbReader.new("吸血鬼", table) threads << DbWriter.new("ハーピー ", table) threads << DbReader.new("妖精", table) threads << DbWriter.new("サラマンダー", table) threads.each {|t|t.join}
実行結果例
write:1:(ハーピー :1), read:トカゲ:1:(ハーピー :1), read:吸血鬼:1:(ハーピー :1), write:1:(人魚:1), write:1:(サラマンダー:1), write:2:(ハーピー :2), write:2:(サラマンダー:2), write:1:(もぐら:1), read:ロブスター:1:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1), read:雪だるま:1:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1), read:トカゲ:2:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1), read:ロブスター:2:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1), read:吸血鬼:2:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1), read:妖精:1:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1), read:雪だるま:2:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1), write:3:(ハーピー :3), read:吸血鬼:3:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1),(ハーピー :3), read:トカゲ:3:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1),(ハーピー :3), read:ロブスター:3:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1),(ハーピー :3), read:妖精:2:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1),(ハーピー :3), read:雪だるま:3:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1),(ハーピー :3), read:吸血鬼:4:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1),(ハーピー :3), read:トカゲ:4:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1),(ハーピー :3), read:妖精:3:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1),(ハーピー :3), write:2:(人魚:2), write:2:(もぐら:2), write:1:(操り人形:1), write:3:(人魚:3), write:2:(操り人形:2), read:妖精:4:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1),(ハーピー :3),(人魚:2),(もぐら:2),(操り人形:1),(人魚:3),(操り人形:2), read:トカゲ:5:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1),(ハーピー :3),(人魚:2),(もぐら:2),(操り人形:1),(人魚:3),(操り人形:2), read:ロブスター:4:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1),(ハーピー :3),(人魚:2),(もぐら:2),(操り人形:1),(人魚:3),(操り人形:2), read:雪だるま:4:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1),(ハーピー :3),(人魚:2),(もぐら:2),(操り人形:1),(人魚:3),(操り人形:2), read:ロブスター:5:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1),(ハーピー :3),(人魚:2),(もぐら:2),(操り人形:1),(人魚:3),(操り人形:2), read:妖精:5:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1),(ハーピー :3),(人魚:2),(もぐら:2),(操り人形:1),(人魚:3),(操り人形:2), read:吸血鬼:5:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1),(ハーピー :3),(人魚:2),(もぐら:2),(操り人形:1),(人魚:3),(操り人形:2), write:4:(人魚:4), write:3:(操り人形:3), write:3:(サラマンダー:3), write:4:(ハーピー :4), write:3:(もぐら:3), write:5:(人魚:5), write:4:(もぐら:4), write:5:(ハーピー :5), write:4:(サラマンダー:4), read:雪だるま:5:(ハーピー :1),(人魚:1),(サラマンダー:1),(ハーピー :2),(サラマンダー:2),(もぐら:1),(ハーピー :3),(人魚:2),(もぐら:2),(操り人形:1),(人魚:3),(操り人形:2),(人魚:4),(操り人形:3),(サラマンダー:3),(ハーピー :4),(もぐら:3),(人魚:5),(もぐら:4),(ハーピー :5),(サラマンダー:4), write:4:(操り人形:4), write:5:(もぐら:5), write:5:(操り人形:5), write:5:(サラマンダー:5),