読み取り/書き込みロックを使用すると、同時に書き込み操作ができるスレッドを 1 つだけに制限する一方、読み取り操作は同時に複数のスレッドからできるようになります。この節の内容は次のとおりです。
読み取り/書き込みロックの初期化
読み取りロックの獲得
読み取りロックの獲得の試行
書き込みロックの獲得
書き込みロックの獲得の試行
読み取り/書き込みロックの解除
読み取り/書き込みロックの状態の削除
すでに読み取りロックを保持しているスレッドがある場合、ほかのスレッドがさらに読み取りロックを獲得できますが、書き込みロックを獲得するときは待たなければなりません。すでに書き込みロックを保持しているスレッドがある場合、あるいは書き込みロックの獲得を待っているスレッドがある場合、ほかのスレッドは読み取りと書き込みのどちらのロックを獲得するときも待たなければなりません。
読み取り/書き込みロックは、相互排他ロックよりも低速です。しかし、読み取り/書き込みロックでは、書き込みの頻度が低く、かつ多数のスレッドから並行的に読み取られるようなデータを保護するときの性能を改善することができます。
このプロセス内のスレッドと別のプロセス内のスレッド間で同期をとるには、読み取り/書き込みロックを使用します。連携するそれらのプロセスの間で共有される書き込み可能なメモリーに、読み取り/書き込みロックの領域を確保します。この動作の読み取り/書き込みロックのマッピングについては、mmap(2) のマニュアルページを参照してください。
複数のスレッドが読み取り/書き込みロックを待っている場合のロックの獲得順序は、特に指定しなければ不定です。ただし、書き込み側がいつまでもロックを獲得できないような事態を回避するため、Solaris スレッドパッケージでは書き込み側が同じ優先順位の読み取り側より優先されます。
読み取り/書き込みロックは、使用する前に初期化する必要があります。
rwlp が指す読み取り/書き込みロックを初期化して、ロック状態を解除するには、rwlock_init(3C) を使用します。
#include <synch.h> (or #include <thread.h>) int rwlock_init(rwlock_t *rwlp, int type, void * arg);
type は、次のいずれかになります。
USYNC_PROCESS 読み取り/書き込みロックを使って、このプロセスとほかのプロセスのスレッド間で同期をとることができます。arg は無視されます。
USYNC_THREAD 読み取り/書き込みロックを使って、このプロセス内のスレッドの間だけで同期をとることができます。arg は無視されます。
複数のスレッドから同じ読み取り/書き込みロックを同時に初期化してはいけません。0 に初期化したメモリーに領域を確保することによって、読み取り/書き込みロックを初期化することもできます。その場合は、type に USYNC_THREAD を指定したものとみなされます。一度初期化した読み取り/書き込みロックは、ほかのスレッドで使われている可能性があるので再初期化してはいけません。
POSIX スレッドの場合については、「pthread_rwlock_init の構文」を参照してください。
#include <thread.h> rwlock_t rwlp; int ret; /* to be used within this process only */ ret = rwlock_init(&rwlp, USYNC_THREAD, 0);
#include <thread.h> rwlock_t rwlp; int ret; /* to be used among all processes */ ret = rwlock_init(&rwlp, USYNC_PROCESS, 0);
rwlock_init()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。
EINVAL
説明:引数が無効です。
EFAULT
説明:rwlp または arg が不当なアドレスを指しています。
rwlp が指す読み取り/書き込みロック上で読み取りロックを獲得するには、rw_rdlock(3C) を使用します。
#include <synch.h> (or #include <thread.h>) int rw_rdlock(rwlock_t *rwlp);
指定した読み取り/書き込みロックが書き込み用にすでにロックされている場合、呼び出しスレッドは書き込みロックが解放されるまでブロックされます。そうでなければ、呼び出しスレッドは読み取りロックを獲得します。POSIX スレッドの場合については、「pthread_rwlock_rdlock の構文」を参照してください。
rw_rdlock()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。
EINVAL
説明:引数が無効です。
EFAULT
説明:rwlp が指すアドレスが正しくありません。
rwlp が指す読み取り/書き込みロック上に読み取りロックを獲得しようとするには、rw_tryrdlock(3C) を使用します。
#include <synch.h> (or #include <thread.h>) int rw_tryrdlock(rwlock_t *rwlp);
読み取り/書き込みロックが書き込み用にロックされているとき、rw_tryrdlock() はエラーを返します。そうでなければ、呼び出しスレッドは読み取りロックを獲得します。POSIX スレッドの場合については、「pthread_rwlock_tryrdlock の構文」を参照してください。
rw_tryrdlock()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。
EINVAL
説明:引数が無効です。
EFAULT
説明:rwlp が指すアドレスが正しくありません。
EBUSY
説明:rwlp が指す読み取り/書き込みロックがすでにロックされています。
rwlp が指す読み取り/書き込みロック上で書き込みロックを獲得するには、rw_wrlock(3C) を使用します。
#include <synch.h> (or #include <thread.h>) int rw_wrlock(rwlock_t *rwlp);
指定した読み取り/書き込みロックが、読み取りまたは書き込み用にすでにロックされている場合、呼び出しスレッドは、すべての読み取りロックと書き込みロックが解放されるまでブロックされます。読み取り/書き込みロックの書き込みロックを保持できるスレッドは一度に 1 つに限られます。POSIX スレッドの場合については、「pthread_rwlock_wrlock の構文」を参照してください。
rw_wrlock()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。
EINVAL
説明:引数が無効です。
EFAULT
説明:rwlp が指すアドレスが正しくありません。
rwlp が指す読み取り/書き込みロック上に読み取りロックを獲得しようとするには、rw_trywrlock(3C) を使用します。
#include <synch.h> (or #include <thread.h>) int rw_trywrlock(rwlock_t *rwlp);
読み取り/書き込みロックが読み取りまたは書き込み用にロックされているとき、rw_trywrlock() はエラーを返します。POSIX スレッドの場合については、「pthread_rwlock_trywrlock の構文」を参照してください。
rw_trywrlock()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。
EINVAL
説明:引数が無効です。
EFAULT
説明:rwlp が指すアドレスが正しくありません。
EBUSY
説明:rwlp が指す読み取り/書き込みロックがすでにロックされています。
rwlp が指す読み取り/書き込みロックを解除するには、rw_unlock(3C) を使用します。
#include <synch.h> (or #include <thread.h>) int rw_unlock(rwlock_t *rwlp);
解除の対象となる読み取り/書き込みロックは、ロックされていて、呼び出しスレッドが読み取り用または書き込み用に保持しているものでなければなりません。その読み取り/書き込みロックが使用可能になるのを待っているスレッドがほかにある場合は、そのスレッドのうちの 1 つがブロック解除されます。POSIX スレッドの場合については、「pthread_rwlock_unlock の構文」を参照してください。
rw_unlock()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。
EINVAL
説明:引数が無効です。
EFAULT
説明:rwlp が指すアドレスが正しくありません。
rwlp が指す読み取り/書き込みロックに関連付けられた状態を削除するには、rwlock_destroy(3C) を使用します。
#include <synch.h> (or #include <thread.h>) int rwlock_destroy(rwlock_t *rwlp);
読み取り/書き込みロックの記憶領域は解放されません。POSIX スレッドの場合については、「pthread_rwlock_destroy の構文」を参照してください。
例 6–1 では、銀行口座の例を使って、読み取り/書き込みロックについて説明します。口座残高に対して複数のスレッドが並行的に読み取り専用アクセスできますが、書き込みは 1 つのスレッドだけに制限されます。get_balance() 関数中のロックは、当座預金の残高 (checking_balance) と普通預金の残高 (saving_balance) を合計する演算が、不可分操作によって確実に行われるために必要です。
rwlock_t account_lock; float checking_balance = 100.0; float saving_balance = 100.0; ... rwlock_init(&account_lock, 0, NULL); ... float get_balance() { float bal; rw_rdlock(&account_lock); bal = checking_balance + saving_balance; rw_unlock(&account_lock); return(bal); } void transfer_checking_to_savings(float amount) { rw_wrlock(&account_lock); checking_balance = checking_balance - amount; saving_balance = saving_balance + amount; rw_unlock(&account_lock); }
rwlock_destroy()は、正常終了時に 0 を返します。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。
EINVAL
説明:引数が無効です。
EFAULT
説明:rwlp が指すアドレスが正しくありません。