学习sync.Map的总结


本文摘自网络,作者tjudqx,侵删。


学习原因
1、golang面试中可能会遇到
2、之前看过sync.Map的源码,但是记忆很模糊了,重新温习下

源码分析
1、设计思想:个人理解是读写分离,在读多写少的情况下,效率较高。
2、源码阅读

结构体

type Map struct {
    mu Mutex

    // read是原子类型。读取数据时,可以不加锁。
    // 更新数据时,通过cas不加锁更新read中存在的数据。
    read atomic.Value // readOnly

    // 操作dirty时,必须持有mutex。有两种状态:
    // 1、nil。此时sync.Map保存的所有数据在read中。
    // 2、不为nil。此时,dirty保存sync.Map所有的数据,且 dirty存在read中map没有的值。

    dirty map[interface{}]*entry

    // misses用于记录,dirty中包含read中不存在的值时,read中没命中的次数
    // 当 misses == len(dirty)时,用dirty更新 read中的map,并将dirty置为nil

    misses int
}

read的底层数据结构

type readOnly struct {
    //  和dirty相同的map结构
    m       map[interface{}]*entry  

    // true 表示dirty中包含 m中没有的值
    amended bool 
}

读数据操作 Load函数

func (m *Map) Load(key interface{}) (value interface{}, ok bool) {
    // 将read转换为它的底层数据结构
    read, _ := m.read.Load().(readOnly)
    // 从readOnly结构体中的map读取
    e, ok := read.m[key]
    // read中不存在,而且dirty中包含read中没有的值
    if !ok && read.amended {
        m.mu.Lock()
        // 加锁,重新从read中读,避免dirty中数据已更新到 read中。
        read, _ = m.read.Load().(readOnly)
        e, ok = read.m[key]
        // read中不存在,而且dirty中包含read中没有的值
        if !ok && read.amended {
            e, ok = m.dirty[key]
            // 只要读了dirty,无论结果,misses+1。
            m.missLocked()
        }
        // 释放锁
        m.mu.Unlock()
    }
    if !ok {
        return nil, false
    }
    return e.load()
}

写操作 Store函数

func (m *Map) Store(key, value interface{}) {
    read, _ := m.read.Load().(readOnly)
    // read中存在目标key,tryStore:若key未被标记为清除, 用cas更新值。
    if e, ok := read.m[key]; ok && e.tryStore(&value) {
        return
    }

    m.mu.Lock()  // 加锁
    read, _ = m.read.Load().(readOnly)
    if e, ok := read.m[key]; ok {
        if e.unexpungeLocked() {
            // The entry was previously expunged, which implies that there is a
            // non-nil dirty map and this entry is not in it.
            m.dirty[key] = e
        }
        e.storeLocked(&value)
    } else if e, ok := m.dirty[key]; ok {
        // dirty已经存在key,直接原子更新对应值
        e.storeLocked(&value)
    } else {
        if !read.amended {
            // We're adding the first new key to the dirty map.
            // Make sure it is allocated and mark the read-only map as incomplete.
            // 此时dirty为nil,第一次向dirty中添加新key-value
            m.dirtyLocked()
            // 此时dirty包含read中没有的key-value,将amended置为true
            m.read.Store(readOnly{m: read.m, amended: true})
        }
        m.dirty[key] = newEntry(value)
    }
    m.mu.Unlock()
}


func (m *Map) dirtyLocked() {
    // dirty不为空直接返回
    if m.dirty != nil {
        return
    }

    read, _ := m.read.Load().(readOnly)
    // dirty初始化,长度为read中map的长度
    m.dirty = make(map[interface{}]*entry, len(read.m))
    //  将read中没标记为清除的key val 放入dirty中
    for k, e := range read.m {
        if !e.tryExpungeLocked() {
            m.dirty[k] = e
        }
    }
}

以上只是个人理解,如发现错误或其他问题,还请不吝赐教!




相关阅读 >>

聊聊tempodb的pool

beeGo项目和Go项目 打包部署到linux

Golang可以写网站吗

Go实现进制之间的转化

关于Golang-import导入包语法

markdown 自定义的思考

Golang map判断key是否存在

聊聊dubbo-Go-proxy的timeoutfilter

Golang 冒泡排序

Go后offer之路上的那些真相

更多相关阅读请进入《Go》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...