并发安全map —— sync.Map
go原生的map不是线程安全的,在并发读写的时候会触发concurrent map read and map write的panic。
map应该是属于go语言非常高频使用的数据结构。早期go标准库并没有提供线程安全的map,开发者只能自己实现,后面go吸取社区需求提供了线程安全的map——sync.Map。
sync.Map 提供5个如下api:
    func (m *Map) Delete(key interface{})      //删除这个key的value
    func (m *Map) Load(key interface{}) (value interface{}, ok bool) //加载这个key的value
    func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) //原子操作加载,如果没有则存储
    func (m *Map) Range(f func(key, value interface{}) bool) //遍历kv
    func (m *Map) Store(key, value interface{}) //存储
使用sync.Map
package main
import (
    "fmt"
    "sync"
)
func main() {
    sMap := sync.Map{}
    sMap.Store("a","b")
    ret,_:= sMap.Load("a")
    fmt.Printf("ret1 %t \n",ret.(string) == "b" )
    ret,loaded :=sMap.LoadOrStore("a","c")
    fmt.Printf("ret2 %t loaded:%t \n",ret.(string) == "b",loaded )
    ret,loaded =sMap.LoadOrStore("d","c")
    fmt.Printf("loaded %t \n",loaded)
    sMap.Store("e","f")
    sMap.Delete("e")
    sMap.Range(func(key, value interface{}) bool {
        fmt.Printf("k:%s v:%s \n", key.(string),value.(string))
        return true
    })
}
$ go run main.go
ret1 true 
ret2 true loaded:true 
loaded false 
k:a v:b 
k:d v:c 
sync.Map底层实现
sync.Map的结构体
type Map struct {
    mu Mutex  //互斥锁保护dirty
    read atomic.Value //存读的数据,只读并发安全,存储的数据类型为readOnly
    dirty map[interface{}]*entry //包含最新写入的数据,等misses到阈值会上升为read
    misses int      //计数器,当读read的时候miss了就加+
}
type readOnly struct {
    m  map[interface{}]*entry 
    amended bool //dirty的数据和这里的m中的数据有差异的时候true
}
从结构体上看Map有一定指针数据冗余,但是因为是指针数据,所以冗余的数据量不大。
Load的源码:
func (m *Map) Load(key interface{}) (value interface{}, ok bool) {
    read, _ := m.read.Load().(readOnly) //读只读的数据
    e, ok := read.m[key]
    if !ok && read.amended { //如果没有读到且read的数据和drity数据不一致的时候
        m.mu.Lock()
        read, _ = m.read.Load().(readOnly) //加锁后二次确认
        e, ok = read.m[key]
        if !ok && read.amended { //如果没有读到且 read的数据和drity数据不一致的时候
            e, ok = m.dirty[key]
            m.missLocked()     //misses +1,如果 misses 大于等于 m.dirty 则发送 read的值指向ditry
        }
        m.mu.Unlock()
    }
    if !ok {
        return nil, false
    }
    return e.load()
}
Store的源码:
func (m *Map) Store(key, value interface{}) {
    read, _ := m.read.Load().(readOnly)
    if e, ok := read.m[key]; ok && e.tryStore(&value) { //如果在read中找到则尝试更新,tryStore中判断key是否已经被标识删除,如果已经被上传则更新不成功
        return
    }
    m.mu.Lock()
    read, _ = m.read.Load().(readOnly) //同上二次确认
    if e, ok := read.m[key]; ok {
        if e.unexpungeLocked() {// 如果entry被标记expunge,则表明dirty没有key,可添加入dirty,并更新entry。
            m.dirty[key] = e
        }
        e.storeLocked(&value)
    } else if e, ok := m.dirty[key]; ok { //如果dirty存在该key
        e.storeLocked(&value)
    } else { //key不存在
        if !read.amended { //read 和 dirty 一致
          	// 将read中未删除的数据加入到dirty中
            m.dirtyLocked()
             // amended标记为read与dirty不一致,因为即将加入新数据。
            m.read.Store(readOnly{m: read.m, amended: true})
        }
        m.dirty[key] = newEntry(value)
    }
    m.mu.Unlock()
}
Delete的源码:
// Delete deletes the value for a key.
func (m *Map) Delete(key interface{}) {
    read, _ := m.read.Load().(readOnly)
    e, ok := read.m[key]
    if !ok && read.amended {   //在read中没有找到且 read和dirty不一致
        m.mu.Lock()
        read, _ = m.read.Load().(readOnly) //加锁二次确认
        e, ok = read.m[key]
        if !ok && read.amended {
            delete(m.dirty, key)  //从dirty中删除
        }
        m.mu.Unlock()
    }
    if ok { //如果key在read中存在
        e.delete() //将指针置为nil,标记删除
    }
}
优缺点
优点: 通过read和dirty冗余的方式实现读写分离,减少锁频率来提高性能。
缺点:大量写的时候会导致read读不到数据而进一步加锁读取dirty,同时多次miss的情况下dirty也会频繁升级为read影响性能。 
因此sync.Map的使用场景应该是读多,写少。
总结
本小节介绍了sync.Map的使用,通过源码的方式了解sync.Map的底层实现,同时介绍了它的优缺点,以及使用场景。