Garbage in, garbage out

是什么

sync.map
sync.mapsync.map

有什么用

一般情况下解决并发读写 map 的思路是加一把大锁,或者把一个 map 分成若干个小 map,对 key 进行哈希,只操作相应的小 map。前者锁的粒度比较大,影响效率;后者实现起来比较复杂,容易出错。

sync.map

如何使用

使用非常简单,和普通 map 相比,仅遍历的方式略有区别:

package main

import (
    "fmt"
    "sync"
)

func main()  {
    var m sync.Map
    // 1. 写入
    m.Store("qcrao", 18)
    m.Store("stefno", 20)

    // 2. 读取
    age, _ := m.Load("qcrao")
    fmt.Println(age.(int))

    // 3. 遍历
    m.Range(func(key, value interface{}) bool {
        name := key.(string)
        age := value.(int)
        fmt.Println(name, age)
        return true
    })

    // 4. 删除
    m.Delete("qcrao")
    age, ok := m.Load("qcrao")
    fmt.Println(age, ok)

    // 5. 读取或写入
    m.LoadOrStore("stefno", 100)
    age, _ = m.Load("stefno")
    fmt.Println(age)
}

第 1 步,写入两个 k-v 对;

第 2 步,使用 Load 方法读取其中的一个 key;

第 3 步,遍历所有的 k-v 对,并打印出来;

第 4 步,删除其中的一个 key,再读这个 key,得到的就是 nil;

第 5 步,使用 LoadOrStore,尝试读取或写入 "Stefno",因为这个 key 已经存在,因此写入不成功,并且读出原值。

程序输出:

18
stefno 20
qcrao 18
<nil> false
20

sync.map

源码分析

数据结构

先来看下 map 的数据结构。去掉大段的注释后:

type Map struct {
    mu Mutex
    read atomic.Value // readOnly
    dirty map[interface{}]*entry
    misses int
}

mu
readread
dirtyreaddirtyreaddirtydirtydirtyread
misses
read mapdirty map
key/valuereaddirty
read
// readOnly is an immutable struct stored atomically in the Map.read field.
type readOnly struct {
    m       map[interface{}]*entry
    amended bool // true if the dirty map contains some key not in m.
}

entry
type entry struct {
    p unsafe.Pointer // *interface{}
}

很简单,它是一个指针,指向 value。看来,read 和 dirty 各自维护一套 key,key 指向的都是同一个 value。也就是说,只要修改了这个 entry,对 read 和 dirty 都是可见的。这个指针的状态有三种:

p == nil
p == expunged
interface{}