コンピュータサイエンスにおいて、リーダー・ライター(シングルライターロック[ 1 ] 、マルチリーダーロック[ 2 ] 、プッシュロック[ 3 ]、またはMRSWロック)は、リーダー・ライター問題の1つを解決する同期プリミティブです。RWロックは読み取り専用操作の同時アクセスを許可しますが、書き込み操作には排他アクセスが必要です。つまり、複数のスレッドがデータを並列に読み取ることができますが、データの書き込みや変更には排他ロックが必要です。ライターがデータを書き込んでいる間は、ライターが書き込みを完了するまで、他のすべてのライターとリーダーはブロックされます。一般的な用途としては、アトミックに更新できず、更新が完了するまで無効(他のスレッドが読み取るべきではない)なメモリ内のデータ構造へのアクセスを制御することが挙げられます。
一部のRWロックでは、読み取りモードから書き込みモードへのロックのアトミックなアップグレード、および書き込みモードから読み取りモードへのダウングレードが可能です。[ 4 ]読み取りモードから書き込みモードへのロックのアップグレードはデッドロックが発生しやすいです。これは、読み取りロックを保持している2つのスレッドが両方とも書き込みロックにアップグレードしようとすると、いずれかのスレッドが読み取りロックを解放することによってのみ解消できるデッドロックが発生するためです。書き込みモードのスレッドがなく、読み取りモードに0個以外のスレッドが存在する可能性がある場合に、「書き込みにアップグレードする意図を持った読み取りモード」でロックを取得できるスレッドを1つだけにすることで、デッドロックを回避できます。
RWロックは、読み取りアクセスと書き込みアクセスに対して異なる優先順位ポリシーを設定して設計できます。ロックは、常に読み取りを優先する(読み取り優先)、常に書き込みを優先する(書き込み優先)、または優先順位を指定しない、のいずれかに設計できます。これらのポリシーは、並行処理とリソース枯渇に関して異なるトレードオフをもたらします。
リーダー・ライターロックの実装戦略はいくつか存在し、それらは事前に存在することを前提とした同期プリミティブに還元される。
Raynal は、2 つのミューテックスと 1 つの整数カウンタを使用して R/W ロックを実装する方法を示します。カウンタbは、ブロッキング リーダーの数を追跡します。1 つのミューテックスrはbを保護し、リーダーのみが使用します。もう 1 つのミューテックスg ("global" の略) は、書き込みの相互排他を保証します。これは、あるスレッドによって取得されたミューテックスを別のスレッドによって解放できる必要があることを意味します。以下は、操作の擬似コードです。
初期化
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です。
ロックと解放の操作は次のように実装できます。
読み始める
gをロックします。num_writers_waiting > 0またはwriter_active の 間: wait cond、g [ a ] num_readers_active をインクリメントg の ロックを解除します。
読み取り終了
ロックg num_readers_active をデクリメントします。num_readers_active = 0 の場合: 通知条件(ブロードキャスト)g の ロックを解除します。
書き始める
ロックg num_writers_waiting をインクリメントするnum_readers_active > 0またはwriter_activeがtrue の間: 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.RWMutexGo [ 17 ]std::sync::RwLockRustにおける読み書きロック[ 19 ]mse::recursive_shared_timed_mutexSaferCPlusPlusライブラリにはstd::shared_timed_mutex、の再帰的所有権セマンティクスをサポートするバージョンがありますstd::recursive_mutex。txrwlock.ReadersWriterDeferredLockTwisted用のリーダー/ライターロック[ 20 ]rw_semaphoreLinuxカーネル[ 21 ]use std :: sync :: RwLock ;let 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