golang 协程互斥锁


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: 锁定结束

sync.Map (Go 1.9+)

Go 1.9 标准库提供了内置并发安全的 map,可以安全地被多个 goroutine 并发使用,无需额外的加锁或协调。

import "sync"

var sm sync.Map  // 直接声明即可使用

// 存储
sm.Store("key1",100)

// 读取
if value ,ok := sm.Load("key1");ok {
  fmt.PrintLn("Loaded value",value.(int)) // 注意类型断言
}

// 读取或存储

actual, loaded := sm.LoadOrStore("key2",200)
fmt.Println("LoadOrStore",actual.(int),"loaded?",loaded)

// 删除
sm.Delete("key1")

// 遍历(注意 Range 的函数签名)
sm.Range(func(key , value any) bool{
  fmt.Println("Range:",key.(string),value.(int))

  return true // 返回 true 继续遍历 , false 停止
})

文章作者: costalong
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 costalong !
评论
  目录