Ants源码解读 - SpanLock

Ants 的源码中,internal/sync/spinlock.go 实现了一个自旋锁。通过指数退避算法递增 goroutine 在获取锁失败后的等待时间,来减少资源的争用并提高重试的成功率。

Ants 采用 uint32 这种简单类型作为自旋锁的实现基础,并通过对该变量进行原子操作以确保线程安全。此外,使用 maxBackoff 常量来限制最大的重试等待时间。

type spinLock uint32
const maxBackoff = 16

加锁

在每次尝试加锁时,首先将 backoff 的值初始化为 1。每次获取锁失败后,backoff 的值会翻倍。如果 CAS 操作失败,goroutine 进入循环,在循环中,它会连续调用 runtime.Gosched() backoff 次,以此让出 CPU 时间片。当成功获取锁时,spinLock 的值会被设置为 1

func (sl *spinLock) Lock() {
	backoff := 1
	for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
		for i := 0; i < backoff; i++ {
			runtime.Gosched()
		}
		if backoff < maxBackoff {
			backoff <<= 1
		}
	}
}

释放锁

释放锁的操作相对简单。通过调用 atomic.StoreUint32() 方法,spinLock 会被原子地设置为 0,从而释放锁。

func (sl *spinLock) Unlock() {
	atomic.StoreUint32((*uint32)(sl), 0)
}