多个 goroutine 共用普通 map 时,把读和写都放在同一把锁的保护下。计数递增属于读改写,应在一次写锁内完成。

用结构体把 map 和锁绑在一起
将下面程序保存为 main.go。方法使用指针接收者。
package main
import (
"fmt"
"sync"
)
type Counts struct {
mu sync.RWMutex
values map[string]int
}
func (c *Counts) Inc(key string) {
c.mu.Lock()
defer c.mu.Unlock()
c.values[key]++
}
func (c *Counts) Get(key string) int {
c.mu.RLock()
defer c.mu.RUnlock()
return c.values[key]
}
func main() {
counts := &Counts{values: make(map[string]int)}
var done sync.WaitGroup
for i := 0; i < 8; i++ {
done.Add(1)
go func() {
defer done.Done()
for n := 0; n < 1000; n++ {
counts.Inc("requests")
}
}()
}
done.Wait()
fmt.Println(counts.Get("requests"))
}
map 初始化后通过方法访问。锁开始使用后,不要按值复制包含它的结构体。
核对并发计数
运行完整程序。
go run main.go # 8000
八个 goroutine 各递增一千次,结果应始终为 8000。
再用竞态检测运行一次
在本机支持的情况下执行。
go run -race main.go
正常情况下不会报告数据竞争。新增删除或遍历方法时,也要用同一把锁保护访问。

















