go的互斥锁Mutex

1.基本用法

当多个 goroutine 需要访问共享数据时,可以使用 sync.Mutex 来保护数据。

var counter int = 0
var mu sync.Mutex

func main() {
    for i := 0; i < 1000; i++ {
        go func() {
            mu.Lock()
            counter++
            mu.Unlock()
        }()
    }
    time.Sleep(time.Second)
    fmt.Println(counter)
}

代码运行逻辑

  1. 主 goroutine 启动 1000 个子 goroutine。
  2. 每个子 goroutine 尝试获取互斥锁 mu,如果锁已被其他 goroutine 持有,则阻塞等待。
  3. 成功获取锁的 goroutine 修改 counter,将 counter 加 1。
  4. 修改完成后,释放锁,允许其他 goroutine 继续执行。
  5. 主 goroutine 等待 1 秒(确保所有子 goroutine 完成),然后打印 counter 的最终值。

关键点

  1. 互斥锁的作用
    • 保证同一时间只有一个 goroutine 可以修改 counter,避免了竞争条件。
  2. 竞争条件
    • 如果没有互斥锁,多个 goroutine 同时修改 counter,可能导致最终值小于 1000。
    • 例如,两个 goroutine 同时读取 counter 的值为 10,然后分别加 1,最终 counter 的值是 11 而不是 12。
  3. 等待 goroutine 完成
    • 使用 time.Sleep(time.Second) 是一种简单的等待方式,但不精确。
    • 更推荐使用 sync.WaitGroup 来精确控制 goroutine 的同步。

2. 改进版本(使用 sync.WaitGroup

以下是使用 sync.WaitGroup 的改进版本,可以更精确地等待所有 goroutine 完成:

package main

import (
    "fmt"
    "sync"
)

var counter int = 0
var mu sync.Mutex
var wg sync.WaitGroup

func main() {
    wg.Add(1000) // 设置需要等待的 goroutine 数量
    for i := 0; i < 1000; i++ {
        go func() {
            defer wg.Done() // goroutine 完成后通知 WaitGroup
            mu.Lock()
            counter++
            mu.Unlock()
        }()
    }
    wg.Wait() // 等待所有 goroutine 完成
    fmt.Println(counter)
}

go func() { ... }() 中:

  • func() { ... } 定义了一个匿名函数。
  • () 立即执行这个匿名函数。
  • go 关键字将这个匿名函数的执行放在一个新的 goroutine 中并发运行。
  • 最后的 () 是必不可少的,它确保了匿名函数的立即执行。

3.匿名函数不太习惯,修改为具名函数

如果希望在循环外部单独定义函数,并在循环中执行这个函数,可以将匿名函数定义为具名函数,然后在循环中通过 go 关键字启动它。以下是修改后的代码:

package main

import (
    "fmt"
    "sync"
)

var counter int = 0
var mu sync.Mutex
var wg sync.WaitGroup

// 单独定义函数
func incrementCounter() {
    defer wg.Done() // goroutine 完成后通知 WaitGroup
    mu.Lock()
    counter++
    mu.Unlock()
}

func main() {
    wg.Add(1000) // 设置需要等待的 goroutine 数量
    for i := 0; i < 1000; i++ {
        go incrementCounter() // 在循环中执行定义的函数
    }
    wg.Wait() // 等待所有 goroutine 完成
    fmt.Println(counter)
}

posted @ 2025-02-21 09:31  坚强的小蚂蚁  阅读(21)  评论(0)    收藏  举报