Golang的sync.Map源码解读

sync.Map源码解读

说明:

  1. 在map中,内部有两个map结构dirty和read.m
  2. 在查询过程中会优先从read.m中查询,并且对缺失计数misses加一。当misses达到dirty的长度时,会用dirty来替换read,并重置dirty为nil和misses为0。我称这个过程为缺失处理
  3. 再所有操作中,都会优先访问read.m再访问dirty
type Map struct {
   read atomic.Value // readOnly,存放一个readOnly
   mu Mutex
   dirty map[interface{}]*entry
   misses int		// readOnly获取不到值时加一
}

// 这是一个只读的map
type readOnly struct {
	m       map[interface{}]*entry
	amended bool // // 如果Map.dirty有些数据不在m的时候,这个值为true
}

type entry struct {
	p unsafe.Pointer // 存放值的指针值,实为*int类型
}


 

获取值

大致过程:

  1. 先从read.m中获取,如果不存在且两个dirty和read.m中的数据不一致,则会从dirty中获取,并且做一次缺失处理。
func (m *Map) Load(key interface{}) (value interface{}, ok bool) {
	read, _ := m.read.Load().(readOnly)
	e, ok := read.m[key]

    // key不存在read中,且dirty中含有read没有的值时
    // 这里两个if判断一样时因为使用了双重校验锁模式(java单例模式双重校验锁中常见)
	if !ok && read.amended {
		m.mu.Lock()
		e, ok = read.m[key]
		if !ok && read.amended {
			e, ok = m.dirty[key]    // 从diry中获取
			m.missLocked()          // 进行是否需要覆盖read操作
		}
		m.mu.Unlock()
	}
	if !ok {
		return nil, false
	}
	return e.load()                // 返回指针中存放的数据
}

// 缺失处理
func (m *Map) missLocked() {
	m.misses++        // 缺失加一
	if m.misses < len(m.dirty) {
		return
	}
    // 缺失次数>=dirty长度时
	m.read.Store(readOnly{m: m.dirty})    // 将dirty赋值给read
	m.dirty = nil                         // 同时m。dirty设为nil
	m.misses = 0                          // 次数重置
}

// 获取指针的值
func (e *entry) load() (value interface{}, ok bool) {
	p := atomic.LoadPointer(&e.p)
	if p == nil || p == expunged {
		return nil, false
	}
	return *(*interface{})(p), true
}

设置值

大致流程:

  1. 首先从read.m中获取,如果存在且不是expunged(删除标志),则在read.m中进行修改
  2. 如果存在read.m中且为删除标志,则修改该值并添加进入dirty中
  3. 如果存在dirty中,则修改该值
  4. 如果都不存在,先会判断read和dirty中的数据是否一致,如果一致则会对dirty进行一个检查是否创建和重置read.最后添加到dirty中
func (m *Map) Store(key, value interface{}) {
	read, _ := m.read.Load().(readOnly)
    // 在read中发现key已存在且value不是删除标识,则更新value
	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 {
        // 进入这里标识read中含有该key,但值为删除标识expunged
        // 将e置为空成功后赋值给dirty
		if e.unexpungeLocked() {
			m.dirty[key] = e
		}
        // 并将值地址赋值给e中的指针
		e.storeLocked(&value)
	} else if e, ok := m.dirty[key]; ok {
        // 如果dirty中含有该key则直接赋值
		e.storeLocked(&value)
	} else {
        // dirty和read都没有该key且dirty含有read没有的值则
		if !read.amended {
			m.dirtyLocked()    // 判断dirty是否存在
			m.read.Store(readOnly{m: read.m, amended: true})    // 重置read
		}
		m.dirty[key] = newEntry(value)    // 在drity中设置
	}
	m.mu.Unlock()
}

// entry原子修改指针为i
func (e *entry) tryStore(i *interface{}) bool {
	for {
		p := atomic.LoadPointer(&e.p)
        // 指针为expunged则标识为删除
		if p == expunged {
			return false
		}
		if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) {
			return true
		}
	}
}

func (m *Map) dirtyLocked() {
	if m.dirty != nil {
		return
	}

    // dirty不存在则拷贝read
	read, _ := m.read.Load().(readOnly)
	m.dirty = make(map[interface{}]*entry, len(read.m))
	for k, e := range read.m {
		if !e.tryExpungeLocked() {
			m.dirty[k] = e
		}
	}
}

func (e *entry) unexpungeLocked() (wasExpunged bool) {
	return atomic.CompareAndSwapPointer(&e.p, expunged, nil)
}

func (e *entry) storeLocked(i *interface{}) {
	atomic.StorePointer(&e.p, unsafe.Pointer(i))
}

如果不存在则设置值

说明: 如果已经存在key则返回值,否者设置值并返回设置的值.loaded为true表示已经有值

大致流程:

  1. 先从read.m中获取,如果存在且不是expunge则尝试去修改(因为其他groutine也会进行修改),如果是expunge则会清空并赋值给dirty
  2. 如果read.m中不存在则会去dirty获取,如果存在则尝试修改.如果不存在且read和dirty中数据一致,则会对dirty进行一个检查是否创建和重置read.最后再dirty设置值返回
func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) {
	read, _ := m.read.Load().(readOnly)
	if e, ok := read.m[key]; ok {
		actual, loaded, ok := e.tryLoadOrStore(value)
		if ok {
			return actual, loaded
		}
	}

    // read中e没有有效值则会进入这里
	m.mu.Lock()
	read, _ = m.read.Load().(readOnly)
	if e, ok := read.m[key]; ok {
        // 如果是删除标志则置空后赋值给dirty
		if e.unexpungeLocked() {
			m.dirty[key] = e
		}
        // 此时的e中值为nil,所以会设置值
		actual, loaded, _ = e.tryLoadOrStore(value)
	} else if e, ok := m.dirty[key]; ok {
        // 如果read没有该值,但dirty有该值则进行存储
		actual, loaded, _ = e.tryLoadOrStore(value)
		m.missLocked()
	}  else {
        // 如果read没有该值,dirty也没有该值
        // read重新赋值给dirty,并重置amended
		if !read.amended {
			m.dirtyLocked()
			m.read.Store(readOnly{m: read.m, amended: true})
		}
        // 设置该值
		m.dirty[key] = newEntry(value)
		actual, loaded = value, false
	}
	m.mu.Unlock()

	return actual, loaded
}

// 如果e已经有值则取出返回,否则设置值为i并返回
// loaded为true表示e已经有有效值(自己没有设置e),ok为true表示e有有效值
func (e *entry) tryLoadOrStore(i interface{}) (actual interface{}, loaded, ok bool) {
	p := atomic.LoadPointer(&e.p)
    // 判断值指针是否为删除标识
	if p == expunged {
		return nil, false, false
	}
    // 如果不为空则取出并返回值
	if p != nil {
		return *(*interface{})(p), true, true
	}

	ic := i
	for {
        // 这里值为nil,则设置并返回该值
		if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) {
			return i, false, true
		}

        // 这里之所以会重复前面的操作,是因为修改失败表示其他groutine修改了值,值不为空了
		p = atomic.LoadPointer(&e.p)
		if p == expunged {
			return nil, false, false
		}
		if p != nil {
			return *(*interface{})(p), true, true
		}
	}
}

删除操作

说明:删除指定key,value为删除成功的值,loaded为true表示存在有效值

大致流程:

  1. 先从read.m中获取,如果不存在且dirty存在read.m中没有的值,则从dirty 中查询并删除,同时进行缺失操作
  2. 如果存在则进行删除
func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool) {
	read, _ := m.read.Load().(readOnly)
	e, ok := read.m[key]
    // 如果read不存在该key且dirty含有read中没有的数据
	if !ok && read.amended {
		m.mu.Lock()
		read, _ = m.read.Load().(readOnly)
		e, ok = read.m[key]
		if !ok && read.amended {
            // 在dirty进行删除操作
			e, ok = m.dirty[key]
			delete(m.dirty, key)
			m.missLocked()
		}
		m.mu.Unlock()
	}
	if ok {
        // 如果read中存在则进行删除
		return e.delete()
	}
	return nil, false
}

// 删除值
func (e *entry) delete() (value interface{}, ok bool) {
	for {
		p := atomic.LoadPointer(&e.p)
		if p == nil || p == expunged {
			return nil, false
		}
		if atomic.CompareAndSwapPointer(&e.p, p, nil) {
			return *(*interface{})(p), true
		}
	}
}

遍历执行键值对

如果dirty中含有read.m中没有的值,则会将read.m替换成dirty再进行遍历

func (m *Map) Range(f func(key, value interface{}) bool) {
	read, _ := m.read.Load().(readOnly)
    // 如果dirty含有read中没有的值
	if read.amended {
		m.mu.Lock()
		read, _ = m.read.Load().(readOnly)
		if read.amended {
            // 使用dirty置换read
			read = readOnly{m: m.dirty}
			m.read.Store(read)
			m.dirty = nil
			m.misses = 0
		}
		m.mu.Unlock()
	}

    // 遍历执行,函数返回false则停止执行
	for k, e := range read.m {
		v, ok := e.load()
		if !ok {
			continue
		}
		if !f(k, v) {
			break
		}
	}
}

// 获取值
func (e *entry) load() (value interface{}, ok bool) {
	p := atomic.LoadPointer(&e.p)
	if p == nil || p == expunged {
		return nil, false
	}
	return *(*interface{})(p), true
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值