コンピュータサイエンスにおいて、リーダー・ライター(シングルライターロック、[ 1 ]マルチリーダーロック、[ 2 ]プッシュロック、[ 3 ]またはMRSWロック)は、リーダー・ライター問題の1つを解決する同期プリミティブです。RWロックは読み取り専用操作の同時アクセスを許可しますが、書き込み操作には排他アクセスが必要です。つまり、複数のスレッドが並行してデータを読み取ることができますが、データの書き込みまたは変更には排他ロックが必要です。ライターがデータを書き込んでいるとき、ライターが書き込みを完了するまで、他のすべてのライターとリーダーはブロックされます。一般的な使用法としては、更新が完了するまでアトミックに更新できず無効な(別のスレッドによって読み取られるべきではない)メモリ内のデータ構造へのアクセスを制御することが挙げられます。
リーダー/ライター ロックは通常、ミューテックスと条件変数、またはセマフォの上に構築されます。
一部のRWロックでは、ロックを読み取りモードから書き込みモードへアトミックにアップグレードしたり、書き込みモードから読み取りモードへダウングレードしたりすることができます。[ 4 ]読み取りモードから書き込みモードへのロックのアップグレードはデッドロックになりやすいです。これは、読み取りロックを保持している2つのスレッドが両方とも書き込みロックにアップグレードしようとすると、いずれかのスレッドが読み取りロックを解放することによってのみ解除できるデッドロックが発生するためです。デッドロックは、書き込みモードのスレッドが存在せず、読み取りモードのスレッドがゼロ以外の場合、1つのスレッドだけが「書き込みにアップグレードする意図を持つ読み取りモード」でロックを取得できるようにすることで回避できます。
RWロックは、読み取りアクセスと書き込みアクセスに対して異なる優先度ポリシーを持つように設計できます。ロックは、常に読み取りアクセスを優先する(読み取り優先)、常に書き込みアクセスを優先する(書き込み優先)、または優先度を指定しないのいずれかに設計できます。これらのポリシーは、同時実行性とスタベーションに関して異なるトレードオフをもたらします。
リーダー/ライター ロックの実装戦略はいくつか存在し、それらを事前に存在すると想定される同期プリミティブにまで縮小します。
Raynalは、2つのミューテックスと1つの整数カウンタを用いてR/Wロックを実装する方法を実証しています。カウンタb は、ブロックしている読み取り側の数を追跡します。一方のミューテックスrはbを保護し、読み取り側のみが使用します。もう一方のミューテックスg (「グローバル」の略) は、書き込み側の相互排他性を確保します。このため、あるスレッドが取得したミューテックスを別のスレッドが解放できる必要があります。以下は、これらの操作の 擬似コードです。
初期化
bを0に設定します。r はロック解除されます。g はロック解除されます。
読み始める
ロックr。b を増分します。b = 1 の場合、gをロックします。r の ロックを解除します。
読み終える
ロックr。b を減分します。b = 0 の場合、gのロックを解除します。r の ロックを解除します。
書き始める
gをロックします。 書き込み終了
g のロックを解除します。 この実装は読み取り優先です。[ 5 ]:76
あるいは、RWロックは条件変数cond、通常の(ミューテックス)ロックg、現在アクティブまたは待機中のスレッドを表すさまざまなカウンタとフラグを使用して実装することもできます。[ 8 ] [ 9 ] [ 10 ]書き込み優先RWロックの場合は、2つの整数カウンタと1つのブールフラグを使用できます。
最初は、num_readers_activeとnum_writers_waitingはゼロで、writer_activeは false です。
ロックとリリースの操作は次のように実装できます。
読み始める
num_writers_waiting > 0またはwriter_active の間、gをロックします。 wait cond , g [ a ] num_readers_active をインクリメントし、g をロック解除します。
読み終える
ロックg num_readers_active を減分num_readers_active = 0 の場合: 通知条件(ブロードキャスト)g の ロックを解除します。
書き始める
num_readers_active > 0またはwriter_activeがtrue の間、num_writers_waiting を増分します。 wait cond , g num_writers_waiting をデクリメントしますwriter_activeをtrueに 設定しますg の ロックを解除します。
書き込み終了
ロックg writer_activeをfalseに 設定条件 を通知(ブロードキャスト)g の ロックを解除します。
pthread_rwlock_tおよび関連操作[ 11 ]System.Threading.ReaderWriterLockSlimロック[ 13 ]std::shared_mutexC++17の読み取り/書き込みロック[ 14 ]boost::shared_mutexBoost C++ライブラリboost::upgrade_mutexのロック[ 15 ]SRWLockWindows Vista以降、 WindowsオペレーティングシステムAPIに追加された。[ 16 ]sync.RWMutex囲碁において[ 17 ]std::sync::RwLockRustの読み取り/書き込みロック[ 19 ]mse::recursive_shared_timed_mutexSaferCPlusPlusライブラリの はstd::shared_timed_mutex、 の再帰所有権セマンティクスをサポートするのバージョンですstd::recursive_mutex。txrwlock.ReadersWriterDeferredLockTwistedのリーダー/ライターロック[ 20 ]rw_semaphoreLinuxカーネル内[ 21 ]std :: sync :: RwLockを使用します。lock = RwLock :: new ( 5 )とします。// 一度に複数の読み取りロックを保持できます{ let r1 = lock . read (). unwrap (); let r2 = lock . read (). unwrap (); assert_eq! ( * r1 , 5 ); assert_eq! ( * r2 , 5 ); } // この時点で読み取りロックは解除されます// ただし、書き込みロックは 1 つしか保持できません{ let mut w = lock . write (). unwrap (); * w += 1 ; assert_eq! ( * w , 6 ); } // ここで書き込みロックが解除されます読み取り・コピー・更新(RCU)アルゴリズムは、リーダー・ライター問題に対する一つの解決策です。RCUはリーダーを待機させる必要がありません。Linuxカーネルは、少数のライター向けにseqlockと呼ばれる特別な解決策を実装しています。
java.util.concurrent.locks.ReentrantReadWriteLock Javaのリーダー・ライターロック実装は「公平な」モードを提供するjava.util.concurrent.locks.ReadWriteLock