スピンロック
スピンロックの第一の問題点はロックを獲得しようと待っている時間を浪費することである。これを避ける代替方式が2つ存在する。
ロックを獲得しない。多くの場合、データ構造をうまく設計することでロックを使わずに済むようにできる。すなわち、スレッド毎にデータを用意したり、CPU毎にデータを用意して割り込み不可にして使用するなどの手法がある。
待っている間は他のスレッドに切り換える(スリープロックなどと呼ばれる)。一般にスレッドをそのロックを待っているスレッドのキューに登録し、他のスレッドにコンテキストスイッチする。全てのスレッドが確保済みのロックを解放してスリープするならデッドロック(あるいはリソーススタベーション)が発生しにくくなるという利点があり、スケジューリングによって次にどのスレッドにそのロックを獲得させるかを決めることが(ある程度)可能である。
ロックを保持しているスレッドがCPUを手放した後、何らかの原因により再度実行できなくなった場合、他のスレッドは(ロックを繰り返し獲得しようとして)スピンし続けてしまう。その結果、ロックを保持するスレッドがロックを解放するまで、他のスレッドは先に進むことができない。これは indefinite postponement 状態と呼ばれる。シングルプロセッサシステムではこの状態が発生すると直ちにシステムが無限ループに陥る。というのも、他のスレッドが並行して動くことは決してないので、いったんスピンし始めると永久にスピンし続けることになるのである。マルチプロセッサシステムではすぐには致命的な問題にはならないものの、システム全体の並列度が徐々に低下する。すべてのCPUが決して解放されないスピンロック待ちのスレッドで埋まった時点で、システムはシングルプロセッサと同様動作しなくなる。このような問題を防ぐため、スピンロックを所有しているスレッドに対しては一般に以下の制限を課す。
スレッドが実行しているCPUでの割り込みおよび例外処理を禁止する。やむを得ず許可する場合は、割り込まれたスレッドがCPUを奪われないことを保証する。
スレッドが自発的にスリープする場合は、スピンロックを解放する。 スレッドのスリープは、別のスレッドにスリープを終了させる挙動を期待することを意味する。そのような挙動を実行するためには同じスピンロックの獲得が不可欠なので、この制限は自然なものである。
上記の制限により理論的にはデッドロックを避けることができる。
CPU間のバストラフィックを低減するため、ロックを獲得できなかったときのループではロックの値が変化するまでメモリへの書き込みをすべきでない。MESIプロトコルなどのキャッシュプロトコルでは、これによってロックを含むキャッシュラインが "shared" 状態になるため、CPUはロックを待っている間全くバストラフィックを発生しない。この最適化はCPU毎のキャッシュを持つあらゆるアーキテクチャで有効である。
つまり、上記の例で毎回XCHG命令を実行しながらループするのは得策ではない。一度XCHG命令を実行してだめだった場合、単にロック変数を読むだけのループに移行し、値が変化したときに再度XCHG命令を実行すべきということになる。
SSE2をサポートするx86系CPUでは、電力消費量を減らすために pause 命令を使用できる。上記の例のループの中に pause 命令を挿入すると、消費電力を抑えることができる(サポートしていないCPUでは rep nop と同等であり無視される)。これは「公平さ; fairness」を向上させることにもつながるが、公平さは他のCPUアーキテクチャでも大きな問題である。
シングルプロセッサシステムにあっては、ロックの再帰的な獲得がない限りスピンロックの獲得は1回で必ず成功し、スピンすることは決してない。このため、スピンロックを利用したソフトウェアをシングルプロセッサ向けにビルドする場合、スピンロックの獲得および解放そのものを省略する最適化が可能である。ただし、スピンロックが保護している処理がプリエンプトされない前提で実装されていることが多いため、割り込みの禁止は依然必要となる。
スピンロック(英: spin lock, spinlock)とは、計算機科学におけるロックの一種で、スレッドがロックを獲得できるまで単純にループ(スピン)して定期的にロックをチェックしながら待つ方式。スレッドはその間有益な仕事を何もせずに動作し続けるため、これは一種のビジーウェイト状態を発生させる。獲得されたスピンロックは明示的に解放するまでそのまま確保されるが、実装によってはスレッドがブロック(スリープ)したときに自動的に解放される場合もある。
スレッドが短時間だけブロックされるならば、スピンロックは効率的であり、オペレーティングシステムのプロセススケジューリングのオーバーヘッドを防ぐことにもなる。このため、スピンロックはカーネル内でよく使われる。特に、スケジューラ自身のロックにあってはスリープ可能なロックを使用するとそれが再帰的にスケジューラに依存してしまうことにより実装や動作を混乱させる恐れがあることから、何らかの形でスピンロックを使用することが必須となる。
スピンロックは、コンボイ(英語版)の問題を自然に避ける。定義により、スピンロックの獲得に成功したスレッドは現にCPU上にて実行していることによる。そのようなスレッドは獲得したスピンロックが保護している処理を遅延なく実行でき、別途スケジューリングを待つ必要がない。
スピンロックの重要な制限として、スピンロックを所有しているスレッドは、自発的なスリープ、プリエンプションの如何を問わず、CPUを手放してはならない。これに反した挙動が発生すると、最悪の場合デッドロックに陥る。