マルチスレッドのプログラミング

pthread に相当するものがある同期関数 - 読み取り / 書き込みロック

読み取り / 書き込みロックを使用すると、同時に書き込み操作ができるスレッドを 1 つだけに制限する一方、読み取り操作は同時に複数のスレッドからできるようになります。

すでに読み取りロックを保持しているスレッドがある場合、他のスレッドがさらに読み取りロックを獲得できますが、書き込みロックを獲得するときは待たなければなりません。すでに書き込みロックを保持しているスレッドがある場合、あるいは書き込みロックの獲得を待っているスレッドがある場合、他のスレッドは読み取りと書き込みのどちらのロックを獲得するときも待たなければなりません。

読み取り / 書き込みロックは、相互排他ロックよりも低速です。しかし、書き込みの頻度が低く、かつ多数のスレッドから並行的に読み取られるようなデータを保護するときに特に性能を改善します。

現在のプロセス内のスレッドと他のプロセス内のスレッドの間で、読み取り / 書き込みロックを使って同期をとる場合は、連携するそれらのプロセスの間で共有される書き込み可能なメモリーに、読み取り / 書き込みロックの領域を確保し (mmap(2) のマニュアルページを参照)、その読み取り / 書き込みロックをプロセス間同期用に初期化します。

複数のスレッドが読み取り / 書き込みロックを待っている場合のロックの獲得順序は、特に指定しなければ不定です。ただし、書き込み側がいつまでもロックを獲得できないような事態を回避するため、Solaris スレッドパッケージでは書き込み側が読み取り側より優先されます。

読み取り / 書き込みロックは、使用する前に初期化する必要があります。

読み取り / 書き込みロックの初期化

rwlock_init(3THR)


#include <synch.h>  (または #include <thread.h>)

int rwlock_init(rwlock_t *rwlp, int type, void * arg);

rwlock_init(3THR) は、rwlp が指す読み取り / 書き込みロックを初期化してロック解除状態に設定します。type には次のいずれかを指定できます (arg は現在は無視されます)。(POSIX スレッドについては、「pthread_rwlock_init(3THR)」を参照)。

複数のスレッドから同じ読み取り / 書き込みロックを同時に初期化してはいけません。0 に初期化したメモリーに領域を確保することによって、読み取り / 書き込みロックを初期化することもできます。その場合は、typeUSYNC_THREAD を指定したものとみなされます。一度初期化した読み取り / 書き込みロックは、他のスレッドで使われている可能性があるので再初期化してはいけません。

プロセス内スコープでの読み取り / 書き込みロックの初期化


#include <thread.h>

rwlock_t rwlp;
int ret;

/* このプロセスの中だけで使用する */
ret = rwlock_init(&rwlp, USYNC_THREAD, 0); 

プロセス間スコープでの読み取り / 書き込みロックの初期化


#include <thread.h>

rwlock_t rwlp;
int ret;

/* すべてのプロセスの間で使用する */
ret = rwlock_init(&rwlp, USYNC_PROCESS, 0); 

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

引数が無効です。


EFAULT

rwlp または arg が無効なアドレスを指しています。

読み取りロックの獲得

rw_rdlock(3THR)


#include <synch.h> (または #include <thread.h>)

int rw_rdlock(rwlock_t *rwlp);

rw_rdlock(3THR) は、rwlp が指す読み取り / 書き込みロックの読み取りロックを獲得します。指定した読み取り / 書き込みロックが書き込み用にすでにロックされている場合、呼び出しスレッドは書き込みロックが解放されるまでブロックされます。そうでなければ、読み取りロックを獲得します。(POSIX スレッドについては、「pthread_rwlock_rdlock(3THR)」を参照)。

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を戻します。


EINVAL

引数が無効です。


EFAULT

rwlp が無効なアドレスを指しています。

読み取りロックの獲得 (ブロックなし)

rw_tryrdlock(3THR)


#include <synch.h>  (または #include <thread.h>)

int rw_tryrdlock(rwlock_t *rwlp);

rw_tryrdlock(3THR) は、rwlp が指す読み取り / 書き込みロックの読み取りロックを獲得しようとします。指定した読み取り / 書き込みロックが書き込み用にすでにロックされている場合は、エラーを戻します。そうでなければ、呼び出しスレッドは読み取りロックを獲得します。(POSIX スレッドについては、「pthread_rwlock_tryrdlock(3THR)」を参照)。

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

引数が無効です。


EFAULT

rwlp が無効なアドレスを指しています。


EBUSY

rwlp が指す読み取り / 書き込みロックがすでにロックされています。

書き込みロックの獲得

rw_wrlock(3THR)


#include <synch.h>  (または #include <thread.h>)

int rw_wrlock(rwlock_t *rwlp);

rw_wrlock(3THR) は、rwlp が指す読み取り / 書き込みロックの書き込みロックを獲得します。指定した読み取り / 書き込みロックが、読み取りまたは書き込み用にすでにロックされている場合、呼び出しスレッドは、すべての読み取りロックと書き込みロックが解放されるまでブロックされます。読み取り / 書き込みロックの書き込みロックを保持できるスレッドは一度に 1 つに限られます。(POSIX スレッドについては、「pthread_rwlock_wrlock(3THR)」を参照)。

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

引数が無効です。


EFAULT

rwlp が不当なアドレスを指しています。

書き込みロックの獲得

rw_trywrlock(3THR)


#include <synch.h>  (または #include <thread.h>)

int rw_trywrlock(rwlock_t *rwlp);

rw_trywrlock(3THR) は、rwlp が指す読み取り / 書き込みロックの書き込みロックを獲得しようとします。指定した読み取り / 書き込みロックが、読み取りまたは書き込み用にすでにロックされている場合はエラーを戻します。(POSIX スレッドについては、「pthread_rwlock_trywrlock(3THR)」を参照)。

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

引数が無効です。


EFAULT

rwlp が無効なアドレスを指しています。


EBUSY

rwlp が指す読み取り / 書き込みロックがすでにロックされています。

読み取り / 書き込みロックの解除

rw_unlock(3THR)


#include <synch.h>  (または #include <thread.h>)

int rw_unlock(rwlock_t *rwlp);

rw_unlock(3THR) は、rwlp が指す読み取り / 書き込みロックのロックを解除します。解除の対象となる読み取り / 書き込みロックは、ロックされていて、呼び出しスレッドが読み取り用または書き込み用に保持しているものでなければなりません。その読み取り / 書き込みロックが使用可能になるのを待っているスレッドが他にある場合は、そのスレッドのうちの 1 つがブロック解除されます。(POSIX スレッドについては、「pthread_rwlock_unlock(3THR)」を参照)。

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

引数が無効です。


EFAULT

rwlp が無効なアドレスを指しています。

読み取り / 書き込みロックの削除

rwlock_destroy(3THR)


#include <synch.h>  (または #include <thread.h>)

int rwlock_destroy(rwlock_t *rwlp);

rwlock_destroy(3THR) は、rwlp が指す読み取り / 書き込みロックを削除します。読み取り / 書き込みロックの記憶領域は解放されません。(POSIX スレッドについては、「pthread_rwlock_destroy(3THR)」を参照)。

戻り値

正常終了時は 0 です。それ以外の戻り値は、エラーが発生したことを示します。以下のいずれかの条件が検出されると、この関数は失敗し、対応する値を返します。


EINVAL

引数が無効です。


EFAULT

rwlp が無効なアドレスを指しています。

読み取り / 書き込みロックの例

例 8-1 では、銀行口座に関する処理で読み取り / 書き込みロックを使用しています。口座残高に対して複数のスレッドが並行的に読み取り専用アクセスできますが、書き込みは 1 つのスレッドだけに制限されます。get_balance() 関数中のロックは、当座預金の残高 (checking_balance) と普通預金の残高 (saving_balance) を合計する演算が、原子操作によって行われることを保証するため必要です。


例 8-1 銀行口座の読み取り / 書き込み


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);
}