スピンロックとミューテックスの違い:待ち方・CPU使用・保持時間・カーネルでの使い方を比較

スピンロックとミューテックスは、どちらも臨界区間に同時に1つのスレッドだけを入れるロックですが、ロックが空くのを待つ方法が違います。スピンロックは、ループでロックの変数を調べ続けるビジーウェイトで待つので、待つ間もCPUを使い続けますが、空いたらすぐに取れます。ミューテックスは、待つスレッドをスリープさせてCPUを手放し、ロックが解放されたらOSに起こしてもらいます。保持時間がごく短い場合や眠れない割り込みハンドラの中ではスピンロック、長く保持する場合や保持中に眠る可能性がある場合はミューテックスを使います。

項目スピンロックミューテックス
待ち方ロックが空くまで、ループで変数を調べ続ける(ビジーウェイト)空いていなければスリープして待ち、解放されたときに起こしてもらう
待つ間のCPU使い続ける手放す。その間、ほかのスレッドがCPUを使える
待ちの手間コンテキストスイッチがなく、空いたらすぐに取れる眠るときと起きるときに、システムコールとコンテキストスイッチの手間がかかる
向いている保持時間ごく短い時間(コンテキストスイッチにかかる時間より短い)長い時間。保持中にI/Oを待つなど、眠る可能性があるとき
保持中に眠ることしてはいけない。待つ側がCPUを使い続けることになるできる
コアが1つのとき待つ間はロックを持つスレッドが動けず、回っても空かない。カーネルではプリエンプションを止めて使う問題なく使える
カーネル内での使い方割り込みハンドラのような眠れない場面(割り込みコンテキスト)でも使える。割り込みとデータを共有するときは、割り込みを禁止してから取る眠る可能性があるので、プロセスコンテキストでだけ使え、割り込みハンドラでは使えない
優先度逆転ロックを持つスレッドが止められると、待つ側が回り続けてしまう。カーネルでは保持中のプリエンプションを禁止して防ぐ優先度継承つきのミューテックスで対策できる
実装の例アトミック命令(test-and-setやcompare-and-swap)で変数を書き換える。普段は通常の読み出しで待つtest-and-test-and-setで、コア間の通信を減らすLinuxのfutexでは、競合がなければユーザ空間のアトミック命令だけでロックし、競合したときだけカーネルに眠らせてもらう。少し回ってから眠る実装もある

数値は代表的な目安で、世代や製品によって異なります。

どんなときに使うか

スピンロック
マルチコアで、ロックを持つ時間がごく短い(コンテキストスイッチの手間より短い)とき。割り込みハンドラなど、眠ることが許されないカーネルの中の処理。
ミューテックス
ロックを持つ時間が長いとき、保持中にI/Oやメモリの確保などで眠る可能性があるとき。ユーザ空間のアプリケーションの、普通の排他制御。
組み合わせ
多くのミューテックスの実装は、競合がなければスリープせずにアトミック命令だけで取り、取れないときに少し回ってから眠るなど、両方の長所を組み合わせています。

よくある質問

スピンロックはCPUをむだに使うのに、なぜ使われるのですか?

ロックを持つ時間がごく短ければ、眠って起きるためのコンテキストスイッチの手間より、少し回って待つほうが速いからです。また、カーネルの割り込みハンドラのように眠ることが許されない場面では、スピンロックしか使えません。

割り込みハンドラでミューテックスを使えないのはなぜですか?

ミューテックスはロックが取れないと待つスレッドを眠らせますが、割り込みハンドラは特定のスレッドとして動いていないので、眠って後で起こしてもらうことができないからです。割り込みハンドラとデータを共有するときは、スピンロックと割り込みの禁止を組み合わせて使います。

futexとは何ですか?

Linuxで、ミューテックスなどを速く実装するための仕組みです。競合がないときは、ユーザ空間でアトミック命令を使ってロックの変数を書き換えるだけで済ませ、競合して待つ必要があるときだけシステムコールでカーネルに眠らせてもらいます。ほとんどのロックが競合しない場合に、システムコールの手間を省けます。

コアが1つのシステムでスピンロックを使うとどうなりますか?

ユーザ空間で回って待っても、ロックを持つスレッドはそのコアで動けないので、自分の割り当て時間を使い切るまでロックは空きません。そのため、コアが1つのシステムのカーネルでは、スピンロックの代わりにプリエンプションや割り込みを禁止して排他制御をします。

理解度チェック

スピンロックの説明として正しいものはどれか。

  1. Aロックが空くまで、スレッドを眠らせてOSに起こしてもらう方式のロック
  2. Bロックが空くまで、ループでロックの変数を調べ続けて待つ方式のロック
  3. Cディスクを回転させてデータを守る仕組み
答えと解説を見る

正解:B. ロックが空くまで、ループでロックの変数を調べ続けて待つ方式のロック

スピンロックは、アトミック命令でロックを取ろうとし、取れなければすぐにもう一度試すことを繰り返します。OSを呼んで眠ったり起きたりする手間がないので、ロックを持つ時間がごく短い場合に速く動きます。OSのカーネルの中や、割り込みハンドラとの排他などでよく使われます。一方で、待っている間もCPUを使い続けるという欠点があります。

スピンロックが向かない場面として最も適切なものはどれか。

  1. Aロックを持つ時間が長く、待つスレッドがCPUをむだに使い続けてしまう場面
  2. Bロックを持つ時間が数十ナノ秒と非常に短い場面
  3. COSのカーネルの中で、眠ることのできない処理の場面
答えと解説を見る

正解:A. ロックを持つ時間が長く、待つスレッドがCPUをむだに使い続けてしまう場面

スピンロックで待つスレッドは、ロックが空くまでCPUを使い続けます。ロックを持つ時間が長いと、そのCPUの時間がすべてむだになり、ほかのスレッドの実行も妨げます。特にコアが1つしかないと、ロックを持つスレッドが実行されない限り空かないので、待ちは時間の割り当てが終わるまで続きます。長い待ちには、眠って待つミューテックスを使い、短い間だけ回ってから眠る方式もあります。

スピンロックで、ロックの変数をいきなりアトミック命令で書き換えようとするのではなく、普段は通常の読み出しで空くのを待ち、空いたと見えたときだけアトミック命令を使う(test-and-test-and-set)理由として正しいものはどれか。

  1. A通常の読み出しの方がアトミック命令より正確だから
  2. Bアトミック命令は1つのプログラムで1回しか使えないから
  3. C読むだけなら自分のキャッシュの中で待てるので、キャッシュラインがコア間を行き来する通信を大きく減らせるから
答えと解説を見る

正解:C. 読むだけなら自分のキャッシュの中で待てるので、キャッシュラインがコア間を行き来する通信を大きく減らせるから

アトミック命令は書き込みをともなうので、待っているスレッドが繰り返し実行すると、そのたびにラインをM状態で奪い合い、ほかのコアのコピーを無効にし続けます。通常の読み出しなら、各コアはS状態のコピーを自分のキャッシュで読み続けるだけで、通信は起きません。ロックが解放されて値が変わったときだけ、アトミック命令で取りに行きます。

  1. ミューテックス(眠って待つロック)とスピンロックを比べた説明として正しいものはどれか。
  2. RTOSで起こる優先度逆転の説明として正しいものはどれか。
  3. 優先度逆転を防ぐために使われる「優先度継承」の考え方として正しいものはどれか。

マルチコアと同期の練習問題へ

関連する用語の解説

スピンロック
ロックが空くまで、ループでロックの変数を調べ続けて待つ方式のロック。待ちが短いときに速いが、待つ間もCPUを使い続ける。
ミューテックス
同時に1つのスレッドだけが持てるロック。取れないときはスレッドを眠らせてCPUを譲り、解放されるとOSが起こす実装が一般的。
コンテキストスイッチ
CPUで実行するプロセスやスレッドを切り替えること。レジスタやプログラムカウンタなどの状態を保存・復元し、プロセスの切り替えではアドレス空間も切り替える。
アトミック命令
メモリの読み出しと書き込みを、途中でほかのコアに割り込まれない1つの操作として行う命令。CAS、フェッチ・アンド・アッド、LR/SCなど。
優先度逆転
低いタスクが持つ資源を高いタスクが待っている間に、中くらいのタスクが低いタスクを止めて動き続け、高いタスクが長く待たされる現象。

関連する比較