有时候在Go代码中可能会存在多个goroutine同时操作一个资源,这种情况会发生数据竞态问题。
举例:
var x int64
var wg sync.WaitGroup
func add() {
for i := 0; i < 5000; i++ {
x = x + 1
}
wg.Done()
}
func main() {
wg.Add(2)
go add()
go add()
wg.Wait()
fmt.Println(x)
}
上面的代码中开启了 2 个goroutine去累加变量x的值,这 2 个goroutine在访问和修改x变量的时候存在数据竞争,导致最后的结果与期待的不符。
互斥锁
互斥锁是一种常用的控制共享资源访问的方法,它能够保证同时只有一个goroutine可以访问共享资源。Go语言使用sync包的Mutex类型来实现互斥锁。
var lock sync.Mutex
func add() {
for i := 0; i < 5000; i++ {
lock.Lock() //加锁
x = x + 1
lock.Unlock() //解锁
}
wg.Done()
}
使用互斥锁能够保证同一时间有且只有一个goroutine进入临界区,其他的goroutine则在等待;多个goroutine同时等待一个锁时,唤醒的策略是随机的。
读写互斥锁
很多实际的场景下是读多写少,并发的去读取一个资源不涉及修改的时候没有必要加锁,这种场景下使用读写锁是更好的一种选择。读写锁在Go语言中使用sync包的RWMutex类型。