自旋锁简介
在并发编程中,互斥锁(Mutex)是一种常用的同步机制,用于保护临界资源,防止数据竞争。而在某些特定场景下,尤其是当锁的持有时间很短且线程数量有限的情况下,一种更为轻量级的锁——自旋锁(Spin Lock)可以提供更高的性能。
什么是自旋锁
自旋锁是一种忙等待锁,当一个线程尝试获取一个已经被其它线程持有的锁时,这个线程会持续循环检查锁的状态(即“自旋”) ,直到锁被释放后获得所有权。这种等待方式避免了线程上下文切换带来的开销,因此比较适用于锁竞争不激烈且锁定时间非常短的场景。
自旋锁原理
当一个线程尝试获取自旋锁时,如果发现锁已被占用,则该线程会进入一个循环,不断检查锁是否已被释放。一旦锁的持有者完成操作并释放锁后,正在自旋的线程即可立即获得锁并继续执行。
自旋锁的优缺点
避免了线程切换和调度的开销,持锁时间短性能好
持锁时间长或锁竞争频繁会导致CPU
如何选择合适的锁
如果不确定锁的持有时间,则选择互斥锁或者读写锁
如果确定锁的持有时间很短,则可以考虑使用自旋锁
如果读操作比较比多,则优先考虑读写锁
自旋锁实例
package mainimport ("log""sync""sync/atomic"
)func init() {log.SetFlags(log.LstdFlags)
}type SpinLock struct {state int32
}func (s *SpinLock) Lock() {for !atomic.CompareAndSwapInt32(&s.state, 0, 1) {//自旋,不执行任何操作}
}
func (s *SpinLock) unLock() {atomic.StoreInt32(&s.state, 0)
}
func main() {var cnt intspinLock := SpinLock{}wg := sync.WaitGroup{}for i := 0; i < 1000; i++ {wg.Add(1)go func() {spinLock.Lock()cnt++spinLock.unLock()wg.Done()}()}for i := 0; i < 1000; i++ {wg.Add(1)go func() {spinLock.Lock()cnt++spinLock.unLock()wg.Done()}()}wg.Wait()log.Println("cnt:", cnt)
}