Go by Example 中文版: 互斥锁

在前面的例子中,我们看到了如何使用原子操作(atomic-counters)来管理简单的计数器。 对于更加复杂的情况,我们可以使用一个互斥量 来在 Go 协程间安全的访问数据。

package main
import (
    "fmt"
    "sync"
)

Container 中定义了 counters 的 map ,由于我们希望从多个 goroutine 同时更新它, 因此我们添加了一个 互斥锁Mutex 来同步访问。 请注意不能复制互斥锁,如果需要传递这个 struct,应使用指针完成。

type Container struct {
    mu       sync.Mutex
    counters map[string]int
}

在访问 counters 之前锁定互斥锁; 使用 [defer](defer) 在函数结束时解锁。

func (c *Container) inc(name string) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.counters[name]++
}

请注意,互斥量的零值是可用的,因此这里不需要初始化。

func main() {
    c := Container{
        counters: map[string]int{"a": 0, "b": 0},
    }
    var wg sync.WaitGroup

这个函数在循环中递增对 name 的计数

    doIncrement := func(name string, n int) {
        for i := 0; i < n; i++ {
            c.inc(name)
        }
        wg.Done()
    }

同时运行多个 goroutines; 请注意,它们都访问相同的 Container,其中两个访问相同的计数器。

    wg.Add(3)
    go doIncrement("a", 10000)
    go doIncrement("a", 10000)
    go doIncrement("b", 10000)

等待上面的 goroutines 都执行结束

    wg.Wait()
    fmt.Println(c.counters)
}

运行这个程序显示计数器更新和我们期望的一致

$ go run mutexes.go
map[a:20000 b:10000]

接下来我们将看一下,只使用协程和通道, 如何实现相同的任务状态管理。

下一个例子: 状态协程