golang のスピンロックの仕組みを教えて

Go言語でMutexを活用してスピンロックを実現できます.Mutexはsyncパッケージにて定義されており,Lock(), Unlock()の2つのメソッドを備えています.

スピンロックを利用した簡単なサンプルコードを示します。

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

type SpinLock struct {
	flag int32
}

func (l *SpinLock) Lock() {
	for !atomic.CompareAndSwapInt32(&l.flag, 0, 1) {
		// 自旋等待
	}
}

func (l *SpinLock) Unlock() {
	atomic.StoreInt32(&l.flag, 0)
}

func main() {
	var count int
	var wg sync.WaitGroup
	var lock SpinLock

	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()

			lock.Lock()
			defer lock.Unlock()

			count++
		}()
	}

	wg.Wait()
	fmt.Println("Count:", count)
}

上の例では、SpinLock構造体を定義しており、そこでflagフィールドはロックの状態を示すために使われます。Lock()メソッドは原子操作atomic.CompareAndSwapInt32を使用してロックを取得しようと試み、もし成功すると実行を続けますが、さもなければ自転待ちを続けます。Unlock()メソッドはatomic.StoreInt32を使用してロックを解放します。

メイン関数では1000個のgoroutineが作られ、それぞれがロックをかけてアトミックにcountを操作し、最後にcountの値を出力する。スピンロックの使用によりcountの操作はスレッドセーフに保証されている。

スピンロックはロック時間が短く競合の激しい場面で有効ですが、ロック時間が長い、または競合が激しい場合にはパフォーマンスを低下させてしまうおそれがあります。そのため、実際の開発では、状況に応じて適切なロック機構を選択することが重要です。

bannerAds