スピンロックは、主に共有メモリー型のマルチプロセッサ上での使用に適した低レベルの同期機構です。呼び出しスレッドが、すでに別のスレッドによって保持されているスピンロックを要求する場合、2 番目のスレッドは、そのロックが使用可能になったかどうかをテストするためのループに入ります。スピンはプロセッササイクルを浪費するため、ロックを獲得したら、短時間だけ保持するようにすべきです。呼び出し元は、ほかのスレッドがロックを獲得できるようにするためのスリープ操作を呼び出す前に、スピンロックを解除するようにしてください。
スピンロックは mutex と条件変数を使用して実装することもできますが、スピンロックを実行するための標準化された方法は pthread_spin_* 関数です。短期間のロックであれば、pthread_spin_* 関数に必要なオーバーヘッドははるかに少なくなります。
どういうロックを実行する場合も、スレッドのブロックを設定している間に消費されるプロセッサリソースと、ブロックされている間にスレッドによって消費されるプロセッサリソースとの間にトレードオフが発生します。スピンロックでは、スレッドのブロックを設定したあと、単純なループを実行して、ロックが使用可能になるまで不可分なロック処理を繰り返すためのリソースがほとんど必要ありません。スレッドは、待機している間もプロセッサリソースを消費し続けます。
スピンロックに比べると、mutex はスレッドのブロックにより大量のプロセッサリソースを消費します。相互排他ロックが使用できない場合、スレッドはスケジューリングの状態を変更して、自身を待機スレッドの待ち行列に追加します。ロックが使用可能になると、スレッドがロックを獲得する前に、これらの手順を逆にたどる必要があります。スレッドは、ブロックされている間、プロセッサリソースを消費しません。
したがって、スピンロックと mutex は別の目的に使用すると有効な場合があります。非常に短期間のブロックでは、スピンロックの方が全体的なオーバーヘッドは少なくなることがあります。スレッドがより長期間ブロックされる場合は、mutex の方が全体的なオーバーヘッドは少なくなることがあります。
スピンロックを使用するために必要なリソースを割り当て、ロックをロック解除状態に初期化するには、pthread_spin_init(3C) 関数を使用します。
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
#include <pthread.h> pthread_spinlock_t lock; int pshared; int ret; /* initialize a spin lock */ ret = pthread_spin_init(&lock, pshared);
pshared 属性は、次のいずれかの値を持ちます。
PTHREAD_PROCESS_SHARED
説明:スピンロックが割り当てられているメモリーにアクセスできるすべてのスレッドに、スピンロックの操作を許可します。このロックが複数のプロセスによって共有されているメモリーに割り当てられている場合にも、ロックの操作を許可します。
PTHREAD_PROCESS_PRIVATE
説明:スピンロックを、そのスピンロックを初期化したスレッドと同じプロセス内で作成されたスレッドだけが操作できるようにします。異なるプロセスのスレッドからスピンロックを操作しようとした場合、その結果は未定義です。プロセス共有の属性のデフォルト値は、PTHREAD_PROCESS_PRIVATE です。
pthread_spin_init() 関数は、正常終了時に 0 を返します。それ以外の場合は、次のいずれかのエラーコードが返されます。
EAGAIN
説明:別のスピンロックを初期化するために必要なシステムリソースが不足しています。
EBUSY
説明:スピンロックが別のスレッドで使用されている間 (たとえば、pthread_spin_lock() の呼び出しで使用されている間) にそのロックの初期化または削除の試行が検出されました。
EINVAL
説明:lock で指定された値が無効です。
スピンロックを獲得するには、pthread_spin_lock(3C) を使用します。呼び出しスレッドは、ロックが別のスレッドによって保持されていなければ、そのロックを獲得します。それ以外の場合、スレッドは、そのロックが使用可能になるまで pthread_spin_lock() 呼び出しから復帰しません。呼び出し時に、呼び出しスレッドがロックを保持している場合の結果は不定です。
int pthread_spin_lock(pthread_spinlock_t *lock);
#include <pthread.h> pthread_spinlock_t lock; int ret; ret = pthread_ spin_lock(&lock); /* lock the spinlock */
pthread_spin_lock() 関数は、正常終了時に 0 を返します。それ以外の場合は、次のいずれかのエラーコードが返されます。
EDEADLK
説明:現在のスレッドがすでにそのスピンロックを獲得しています。
EINVAL
説明:lock で指定された値が、初期化されたスピンロックオブジェクトを表していません。
スピンロックを獲得し、ロックが別のスレッドによって保持されている場合はただちに失敗するには、pthread_spin_trylock(3C) 関数を使用します。
int pthread_spin_trylock(pthread_spinlock_t *lock);
#include <pthread.h> pthread_spinlock_t lock; int ret; ret = pthread_spin_trylock(&lock); /* try to lock the spin lock */
pthread_spin_trylock() 関数は、正常終了時に 0 を返します。それ以外の場合は、次のいずれかのエラーコードが返されます。
EBUSY
説明:スレッドが現在スピンロックを所有しています。
EINVAL
説明:lock で指定された値が、初期化されたスピンロックオブジェクトを表していません。
獲得されたスピンロックを解放するには、pthread_spin_unlock(3C) 関数を使用します。
int pthread_spin_unlock(pthread_spinlock_t *lock);
#include <pthread.h> pthread_spinlock_t lock; int ret; ret = pthread_spin_unlock(&lock); /* spinlock is unlocked */
pthread_spin_unlock() 関数は、正常終了時に 0 を返します。それ以外の場合は、次のいずれかのエラーコードが返されます。
EPERM
説明:呼び出しスレッドがロックを保持していません。
EINVAL
説明:lock で指定された値が、初期化されたスピンロックオブジェクトを表していません。
スピンロックを削除し、そのロックによって使用されているリソースをすべて解放するには、pthread_spin_destroy(3C) 関数を使用します。
int pthread_spin_destroy(pthread_spinlock_t *lock);
#include <pthread.h> pthread_spinlock_t lock; int ret; ret = pthread_spin_destroy(&lock); /* spinlock is destroyed */
削除したロックを、pthread_spin_init() を呼び出して再び初期化する前に使用した場合、その結果は不定です。スレッドがロックを保持しているときに pthread_spin_destroy() が呼び出された場合、またはスレッドの初期化されていないスピンロックに対してこの関数が呼び出された場合の結果は不定です。
EBUSY
説明:スピンロックが別のスレッドで使用されている間 (たとえば、pthread_spin_lock() の呼び出しで使用されている間) にそのロックの初期化または削除の試行が検出されました。
EINVAL
説明:lock で指定された値が無効です。