go sync.Mutex

182 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第21天,点击查看活动详情

资源竞争

当同一块内存被多个goroutine同时访问,就会产生不知道谁先访问以及无法预料最后结果得情况,此时这块内存称为共享资源,这种情况称为资源竞争。

var sum = 0
func main() {
   //开启1000个协程让sum+10
   for i := 0; i < 1000; i++ {
      go add(10)
   }
   time.Sleep(2 * time.Second)
   fmt.Println("sum:", sum)
}
func add(i int) {
   sum += i
}

image.png

上面得代码通过开启1000个协程让sum加10演示资源竞争的问题,正常情况下应该输出10000,但是事实上输出结果却不可预期。这是因为1000个协程同时访问操作一个变量,有可能某个协程与其他协程一起操作造成结果变小。为了保证并发安全,我们需要保证只有一个协程去执行这个操作。

sync.Mutex

互斥锁指的是在同一时刻只有一个协程执行某段代码,其他协程都要等待该协程执行完毕后才能继续执行。

var (
   sum int
   mutex sync.Mutex
)
func add(i int) {
   mutex.Lock()
   sum += i
   mutex.Unlock()
}

以上被加锁保护的代码又称临界区(访问无法同时被多个协程访问的共享资源的程序片段)。互斥锁有Lock和Unlock两个操作,一个协程获得锁后其他协程只有等锁释放后才能获取这个Lock和Unlock方法。

互斥锁通常成对出现,一般采用defer去保证锁最后被释放。

func add(i int) {
   mutex.Lock()
   defer mutex.Unlock()
   sum += i
}

另外如果上面的程序同时需要读取sum的值,也可以采用互斥锁,这样读取的操作会在操作共享资源之后执行,保证读取的是有效的值。