一、使用方法

Mutext是互斥锁的意思,也叫排他锁,同一时刻一段代码只能被一个线程运行,两个方法Lock(加锁)和Unlock(解锁)

func main() {
 var count = 0
 var wg sync.WaitGroup
 //十个协程数量
 n := 10
 wg.Add(n)
 for i := 0; i < n; i++ {
  go func() {
   defer wg.Done()
   //1万叠加
   for j := 0; j < 10000; j++ {
    count++
   }
  }()
 }
 wg.Wait()
 fmt.Println(count)
}

运行结果如下

38532

添加锁

func main() {
 var count = 0
 var wg sync.WaitGroup
 var mu sync.Mutex
 //十个协程数量
 n := 10
 wg.Add(n)
 for i := 0; i < n; i++ {
  go func() {
   defer wg.Done()
   //1万叠加
   for j := 0; j < 10000; j++ {
    mu.Lock()
    count++
    mu.Unlock()
   }
  }()
 }
 wg.Wait()
 fmt.Println(count)
}                 

运行结果如下,可以看到,已经看到结果变成了正确的100000

二、死锁场景

当两个或两个以上的进程在执行过程中,因争夺资源而处理一种互相等待的状态,如果没有外部干涉无法继续下去,这时我们称系统处于死锁或产生了死锁

1.Lock/Unlock不是成对出现

良好的编程习惯应该是让mu.Lock()和 defer mu.Unlock()成对出现,能有效的降低死锁出现的概率,比如忘了Lock或忘了Unlock,代码建议以下面紧凑的方式出现

mu.Lock()
defer mu.Unlock()

2.锁被拷贝使用

package main

import (
 "fmt"
 "sync"
)

func main() {
 var mu sync.Mutex
 mu.Lock()
 defer mu.Unlock()
 copyTest(mu)
}

//这里复制了一个锁,造成了死锁
func copyTest(mu sync.Mutex) {
 mu.Lock()
 defer mu.Unlock()
 fmt.Println("ok")
}

在函数外层已经加了一个Lock,在拷贝的时候又执行了一次Lock,因此这是一个永远不会获得的死锁

3.循环等待

A等待B,B等待C,C等待A,陷入了无限循环(哲学家就餐问题)

package main

import (
 "sync"
)

func main() {
 var muA, muB sync.Mutex
 var wg sync.WaitGroup

 wg.Add(2)
 go func() {
  defer wg.Done()
  muA.Lock()
  defer muA.Unlock()
  //A依赖B
  muB.Lock()
  defer muB.Lock()
 }()

 go func() {
  defer wg.Done()
  muB.Lock()
  defer muB.Lock()
  //B依赖A
  muA.Lock()
  defer muA.Unlock()
 }()
 wg.Wait()
}

版权声明:本文为chenqionghe原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/chenqionghe/p/13916762.html