読み書きロックの概要
読み書きロックはミューテックスの進化版で、読込ロックを取得した場合、複数スレッドが並列/並行で読み込み操作を実行可能です。これにより読み込み処理時のパフォーマンスが向上します。一方で書き込みロックは排他制御を行うため、ミューテックスと同様にシリアライズされます。
Linuxではpthread_rwlock_t型で定義されます。この型を用いてロックオブジェクトを宣言します:
pthread_rwlock_t data_lock;
ロックオブジェクトは以下の情報を保持します:
- ロック状態(ロック中/解放中)
- 操作種別(読込/書込)
- ロックを保持しているスレッドID
基本的な使用方法はミューテックスと同様で、共有リソースを特定し、臨界セクションの開始時にロック(読込/書込)、終了時にアンロックを行います。
読み書きロックの特性
- 読込ロックは共有可能:複数スレッドが同時に読込操作を実行可能
- 書込ロックは排他制御:1スレッドのみが書き込み許可
- 書込優先:既存の読込ロックが存在する場合でも書込ロック要求が優先
- 読込中は書込不可、書込中は読込不可
適用ケース
すべてのスレッドが書き込み処理しか行わない場合はミューテックスと同等の性能になります。読み込み処理が多数存在する場合に性能向上効果が顕著です。
関数リファレンス
Linuxが提供する読み書きロック関数のプロトタイプ:
#include <pthread.h>
pthread_rwlock_t data_lock;
// 初期化
int pthread_rwlock_init(pthread_rwlock_t *restrict lock, const pthread_rwlockattr_t *restrict attr);
// 破棄
int pthread_rwlock_destroy(pthread_rwlock_t *lock);
引数:
- lock:ロックオブジェクトのアドレス(出力用)
- attr:属性(NULLでデフォルト)
// 読込ロック取得
int pthread_rwlock_rdlock(pthread_rwlock_t *lock);
// ノンブロッキング読込ロック
int pthread_rwlock_tryrdlock(pthread_rwlock_t *lock);
// 書込ロック取得
int pthread_rwlock_wrlock(pthread_rwlock_t *lock);
// ノンブロッキング書込ロック
int pthread_rwlock_trywrlock(pthread_rwlock_t *lock);
// ロック解除(読込/書込共通)
int pthread_rwlock_unlock(pthread_rwlock_t *lock);
使用例
8スレッドがグローバル変数を操作するサンプル。3スレッドは不定期に書き込み、5スレッドは不定期に読み込みを行います。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
int counter = 0;
pthread_rwlock_t data_lock;
// 書き込みスレッド
void* incrementCounter(void* arg)
{
while(1)
{
pthread_rwlock_wrlock(&data_lock);
counter += rand() % 10 + 1;
printf("値更新: %d (スレッドID: %ld)\n", counter, pthread_self());
pthread_rwlock_unlock(&data_lock);
usleep(rand() % 200);
}
return NULL;
}
// 読み込みスレッド
void* displayCounter(void* arg)
{
while(1)
{
pthread_rwlock_rdlock(&data_lock);
printf("現在値: %d (スレッドID: %ld)\n", counter, pthread_self());
pthread_rwlock_unlock(&data_lock);
usleep(rand() % 150);
}
return NULL;
}
int main()
{
pthread_rwlock_init(&data_lock, NULL);
pthread_t writers[3], readers[5];
for(int i=0; i<3; i++) pthread_create(&writers[i], NULL, incrementCounter, NULL);
for(int i=0; i<5; i++) pthread_create(&readers[i], NULL, displayCounter, NULL);
for(int i=0; i<3; i++) pthread_join(writers[i], NULL);
for(int i=0; i<5; i++) pthread_join(readers[i], NULL);
pthread_rwlock_destroy(&data_lock);
return 0;
}