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

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

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

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

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

このプロセス内のスレッドと別のプロセス内のスレッド間で同期をとるには、読み取り/書き込みロックを使用します。連携するそれらのプロセスの間で共有される書き込み可能なメモリーに、読み取り/書き込みロックの領域を確保します。この動作の読み取り/書き込みロックのマッピングについては、mmap(2) のマニュアルページを参照してください。

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

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

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

rwlp が指す読み取り/書き込みロックを初期化して、ロック状態を解除するには、rwlock_init(3C) を使用します。

rwlock_init の構文

#include <synch.h>  (or #include <thread.h>)

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

type は、次のいずれかになります。

複数のスレッドから同じ読み取り/書き込みロックを同時に初期化してはいけません。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 の戻り値

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


EINVAL

説明:

引数が無効です。


EFAULT

説明:

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

読み取りロックの獲得

rwlp が指す読み取り/書き込みロック上で読み取りロックを獲得するには、rw_rdlock(3C) を使用します。

rw_rdlock の構文

#include <synch.h> (or #include <thread.h>)

int rw_rdlock(rwlock_t *rwlp);

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

rw_rdlock の戻り値

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


EINVAL

説明:

引数が無効です。


EFAULT

説明:

rwlp が指すアドレスが正しくありません。

読み取りロックの獲得の試行

rwlp が指す読み取り/書き込みロック上に読み取りロックを獲得しようとするには、rw_tryrdlock(3C) を使用します。

rw_tryrdlock の構文

#include <synch.h>  (or #include <thread.h>)

int rw_tryrdlock(rwlock_t *rwlp);

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

rw_tryrdlock の戻り値

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


EINVAL

説明:

引数が無効です。


EFAULT

説明:

rwlp が指すアドレスが正しくありません。


EBUSY

説明:

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

書き込みロックの獲得

rwlp が指す読み取り/書き込みロック上で書き込みロックを獲得するには、rw_wrlock(3C) を使用します。

rw_wrlock の構文

#include <synch.h>  (or #include <thread.h>)

int rw_wrlock(rwlock_t *rwlp);

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

rw_wrlock の戻り値

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


EINVAL

説明:

引数が無効です。


EFAULT

説明:

rwlp が指すアドレスが正しくありません。

書き込みロックの獲得の試行

rwlp が指す読み取り/書き込みロック上に読み取りロックを獲得しようとするには、rw_trywrlock(3C) を使用します。

rw_trywrlock の構文

#include <synch.h>  (or #include <thread.h>)

int rw_trywrlock(rwlock_t *rwlp);

読み取り/書き込みロックが読み取りまたは書き込み用にロックされているとき、rw_trywrlock() はエラーを返します。POSIX スレッドの場合については、pthread_rwlock_trywrlock の構文」を参照してください。

rw_trywrlock の戻り値

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


EINVAL

説明:

引数が無効です。


EFAULT

説明:

rwlp が指すアドレスが正しくありません。


EBUSY

説明:

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

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

rwlp が指す読み取り/書き込みロックを解除するには、rw_unlock(3C) を使用します。

rw_unlock の構文

#include <synch.h>  (or #include <thread.h>)

int rw_unlock(rwlock_t *rwlp);

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

rw_unlock の戻り値

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


EINVAL

説明:

引数が無効です。


EFAULT

説明:

rwlp が指すアドレスが正しくありません。

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

rwlp が指す読み取り/書き込みロックに関連付けられた状態を削除するには、rwlock_destroy(3C) を使用します。

rwlock_destroy の構文

#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) を合計する演算が、不可分操作によって確実に行われるために必要です。


例 6–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);
}

rwlock_destroy の戻り値

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


EINVAL

説明:

引数が無効です。


EFAULT

説明:

rwlp が指すアドレスが正しくありません。