golang 在多协程的情况下,如果多个协程同时操作一个变量,会出现数据不一致的情况,这个时候就需要使用互斥锁来解决这个问题。
互斥锁 (sync.Mutex)
互斥即不可同时运行。即使用了互斥锁的两个代码片段互相排斥,只有其中一个代码片段执行完成后,另一个才能执行。
Go 标准库中提供了 sync.Mutex 互斥锁类型及其两个方法:
Lock 加锁
Unlock 释放锁
代码案例
下面是没有使用锁一个例子情况:
var setMap = make(map[int]bool,0)
func printOnce(num int) {
if _, exist := setMap[num]; !exist {
fmt.Println(num)
}
setMap[num] = true
}
func main() {
for i := 0; i < 10; i++ {
go printOnce(100)
}
time.Sleep(time.Second)
}
运行 go run main.go 会发生什么情况呢?
go run main.go
100
100
多运行几次打印不同的结果, 有时候打印7次,有时候打印10次,有时候还触发 panic,是因为对同一个数据结构的访问冲突了。解决的方法使用 Lock()
与 UnLock()
的方法解决问题
var m sync.Mutex
var setMap = make(map[int]bool, 0)
func printOnce(num int) {
m.Lock()
if _, exist := setMap[num]; !exist {
fmt.Println(num)
}
setMap[num] = true
m.Unlock()
}
func main() {
for i := 0; i < 10; i++ {
go printOnce(100)
}
time.Sleep(time.Second)
}
运行 go run main.go
go run main.go
100
注意:
一个互斥锁只能同时被一个 goroutine 锁定,其它 goroutine 就算调用 Lock() 方法也会等待锁的释放。
func main() {
ch := make(chan struct{}, 2)
var m sync.Mutex
go func() {
m.Lock()
defer m.Unlock()
fmt.Println("goroutine1: 大概锁定 2s")
time.Sleep(time.Second * 2)
fmt.Println("goroutine1: 锁定结束,准备退出")
ch <- struct{}{}
}()
go func() {
fmt.Println("goroutine2: 等待解锁")
m.Lock()
defer m.Unlock()
fmt.Println("goroutine2: 锁定结束")
ch <- struct{}{}
}()
// 等待 goroutine 执行结束
for i := 0; i < 2; i++ {
<-ch
}
}
运行 go run main.go
go run main.go
goroutine1: 大概锁定 2s
goroutine2: 等待解锁
goroutine1: 锁定结束,准备退出
goroutine2: 锁定结束