首页 > 文章列表 > Go语言中的sync.Mutex如何使用

Go语言中的sync.Mutex如何使用

golang
304 2023-03-08

Go语言中的sync.Mutex如何使用

背景

多个协程操作中经常出现脏读写的情况,这种情况下需要使用互斥锁,保证在对协程共享区域操作的原子性。

如下示例:

启动了 100个协程,每个协程累加 100 次,在没有脏读写的情况下,最后结果应该是 100 * 100 = 10000

package main

import (

	"fmt"

	"sync"

)

func main() {

   var count = 0

   var wg  sync.WaitGroup

   wg.Add(100)

   for i :=0; i< 100; i++ {

      go func(){

         defer wg.Done()

         for j := 0; j< 100; j ++ {

            count ++

         }

      }()

   }

   wg.Wait()

   fmt.Println(count)

}

但是实际结果一直小于 10000

互斥锁

count ++ 操作, 分为三个步骤

在协程的共享区域取出 count 当前值

当前值加一

加一后的值写回协程共享区域

这时需要使用互斥锁, 来保证对 count++ 的三个操作过程中没有其他协程进行读写。

Go的Sync 包提供了Mutex, 读写互斥的锁, 来保证只有一个协程对数据进行读写操作。 以保证 count++操作的原子性

如下示例:

package main

import (

	"fmt"

	"sync"

)

func main() {

   var count = 0

   // 声明Mutex变量

   var mu sync.Mutex

   var wg  sync.WaitGroup

   wg.Add(100)

   for i :=0; i< 100; i++ {

      go func(){

         defer wg.Done()

         for j := 0; j< 100; j ++ {

            // 添加锁

            mu.Lock()

            count ++

            // 解锁

            mu.Unlock()

         }

      }()

   }

   wg.Wait()

   fmt.Println(count)

}

mu.Lock()mu.Unlock() 之间的代码可以保证在操作只会被一个协程执行。这样执行结果就是 10000 了

注意

mu.Lock()mu.Unlock() 必须成对出现,在忘掉 Unlock 的情况下,锁获取后永远不会得到释放,其他 的线程/协程会永远处于阻塞状态,永远获取不到锁,在忘掉 Lock 的情况下,直接 Unlock 一个未加锁的 Mutex,会导致程序 panic。