TL;DR

リソースへのアクセスを制御する仕組みの 1 つで、ロックが解放されるまでループで待ち続ける(スピンする)方式のロック。

なぜ必要か

OS に頼らず、ユーザ空間だけで mutex を実現できる。

仕組み

Wikipedia によると、スピンロックは、ロックを取得できるまでループで状態を確かめ続けるビジーウェイトの一種である。ロックの取得には、test-and-set のようなアトミックな命令が要る。待つ時間が短ければコンテキストスイッチが起きないので効率がよく、OS のスケジューラ自身のロックなど、カーネルの中でよく使われる。

例

Go での実装例。アトミック操作の CAS(Compare-And-Swap)でロックを取得する。

type SpinLock int32
 
func (s *SpinLock) Lock() {
	for !atomic.CompareAndSwapInt32((*int32)(s), 0, 1) {
		// Go scheduler を呼び出して、他のゴルーチンに実行を譲る(yield)
		runtime.Gosched()
	}
}
 
func (s *SpinLock) Unlock() {
	atomic.StoreInt32((*int32)(s), 0)
}
 
func NewSpinLock() sync.Locker {
	var lock SpinLock
	return &lock
}

注意点

ロックを長く占有するなど、リソースの競合が頻繁に起きる場面では、CPU サイクルを無駄に使ってしまう。futex を使えば改善できる。

また、ロックを持ったスレッドが CPU を手放すと、他のスレッドが無駄にスピンし続ける。シングルプロセッサでは、そのまま処理が止まってしまう。

関連

出典