package sync
import (
"sync/atomic"
"unsafe"
)
// Map比起普通的map,是并发安全的;
// Map针对2种情况做了优化:1)多读少写;2)多协程读、写,覆盖不同的key
// 比起map+mutex,Map空间换时间,用2个数据结构read,dirty减少锁的次数
// read为只读,避免读写冲突
// 双重检测机制,且优先从read种读取,因为对read的读取不需要锁
// 动态调整,当misses次数多了后,dirty升级为read
// 延迟删除,删除一个键值只是打标记,在提升dirty时才清除删除的数据
type Map struct {
mu Mutex
// read包含了map种可以安全并发访问的部分(使用/不使用mu)
// Load操作是安全的,store操作需要加mu
read atomic.Value // readOnly
// dirty包含了map中需要加锁才能访问的key
// 被置为enxugunged的entry不会保存在dirty中
// 注意点,dirty为nil,read中的amended为false;dirty不为nil,read的amended为true
dirty map[interface{}]*entry
// misses统计了read中读取key失败,需要加su判断dirty中是否存在key的次数
// 当misses值 >= len(dirty),即加锁消耗达到了拷贝一整个dirty,将dirty升为read
misses int
}
type readOnly struct {
m map[interface{}]*entry
amended bool // true表dirty中包含read不存在的key
}
// 该指针用于标记dirty.map中的一个entry已删除;read中一个entry已删除,则置为nil
var expunged = unsafe.Pointer(new(interface{}))
type entry struct {
// p指向存储的interface
// p == nil 说明entry在read中以被删,但dirty中还在,可以直接更新值
// p == expunged 说明该entry不存在,在更新时需要加到entry
// else 该entry有效,且记录在read中,若dirty不空,则也在dirty中
// 一个entry可以通过原子操作替换为nil来删除
// 一个entry对应的值,可以通过原子操作来替换
p unsafe.Pointer // *interface{}
}
// 为i新建一个entry,其中p保存i的地址
func newEntry(i interface{}) *entry {
return &entry{p: unsafe.Pointer(&i)}
}
// 读取key的值
func (m *Map) Load(key interface{}) (value interface{}, ok bool) {
// 先尝试从read中读取
read, _ := m.read.Load().(readOnly)
e, ok := read.m[key]
// read中不存在,且dirty中包含该key
if !ok && read.amended {
m.mu.Lock()
// 双重检测,因为加锁过程中dirty可能升为read,且dirty被置为nil
read, _ = m.read.Load().(readOnly)
e, ok = read.m[key]
// read不存在该key且dirty中包含key
if !ok && read.amended {
e, ok = m.dirty[key]
// 记录一次未命中,足够多时dirty升级为read
m.missLocked()
}
m.mu.Unlock()
}
// 仍不存在,返回
if !ok {
return nil, false
}
// 通过entry加载数据
return e.load()
}
func (e *entry) load() (value interface{}, ok bool) {
// 原子地重新读取数据
p := atomic.LoadPointer(&e.p)
//若为nil,expunged 说明已经被删
if p == nil || p == expunged {
return nil, false
}
//转为接口指针
return *(*interface{})(p), true
}
// Store sets the value for a key.
func (m *Map) Store(key, value interface{}) {
// 先从read中读取,存在则尝试储存,成功则返回
read, _ := m.read.Load().(readOnly)
if e, ok := read.m[key]; ok && e.tryStore(&value) {
return
}
m.mu.Lock()
// 双重检测,因为加锁过程中dirty可能升为read,且dirty被置为nil
read, _ = m.read.Load().(readOnly)
if e, ok := read.m[key]; ok {// read中存在key
if e.unexpungeLocked() {// 标记为expunged(并重标为nil)
// 此时说明dirty中不存在该key
m.dirty[key] = e
}
// 修改entry的值,修改后read,dirty都修改了(因为保存的是entry的指针)
e.storeLocked(&value)
} else if e, ok := m.dirty[key]; ok {// dirty中存在
e.storeLocked(&value)
} else {// dirty中也没
// 若dirty也没,说明dirty未新建或重建
if !read.amended {
// 新建dirty
m.dirtyLocked()
// 新建read, amended = true
m.read.Store(readOnly{m: read.m, amended: true})
}
// dirty中为该key新建entry
m.dirty[key] = newEntry(value)
}
m.mu.Unlock()
}
// 尝试储存值(多协程操作,需要for使用CAS)
// 若已删除,返回false
// 未删除(包括nil),使用CAS赋值
func (e *entry) tryStore(i *interface{}) bool {
for {
p := atomic.LoadPointer(&e.p)
if p == expunged {
return false
}
if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) {
return true
}
}
}
// 判断entry是否未expunged(此时mu已锁)
// 若为expunged,置为nil,返回true,表示不存在
// 否则返回false
func (e *entry) unexpungeLocked() (wasExpunged bool) {
return atomic.CompareAndSwapPointer(&e.p, expunged, nil)
}
// 为该entry储存值i,为原子操作,调用时e.p不能为expunged
func (e *entry) storeLocked(i *interface{}) {
atomic.StorePointer(&e.p, unsafe.Pointer(i))
}
// 若key存在,则返回对应的value,loaded为true
// 若key不存在,则存储给定的value值;loaded为false
func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) {
// 先尝试从read中读取,若read中存在,则尝试loadorstore,成功则返回
read, _ := m.read.Load().(readOnly)
if e, ok := read.m[key]; ok {
actual, loaded, ok := e.tryLoadOrStore(value)
if ok {
return actual, loaded
}
}
m.mu.Lock()
// 双重检测,因为加锁过程中dirty可能升为read,且dirty被置为nil
read, _ = m.read.Load().(readOnly)
if e, ok := read.m[key]; ok {// read中存在key
if e.unexpungeLocked() {// 标记为expunged(并重标为nil)
// 此时说明dirty中不存在该key
m.dirty[key] = e
}
// 更新后read,dirty中都是最新的
actual, loaded, _ = e.tryLoadOrStore(value)
} else if e, ok := m.dirty[key]; ok {// dirty中存在
// 只更新dirty
actual, loaded, _ = e.tryLoadOrStore(value)
m.missLocked()
} else {// dirty中也没
// 若dirty也没,说明dirty未新建或重建
if !read.amended {
// 新建dirty
m.dirtyLocked()
// 新建read, amended = true
m.read.Store(readOnly{m: read.m, amended: true})
}
// dirty中为该key新建entry
m.dirty[key] = newEntry(value)
actual, loaded = value, false
}
m.mu.Unlock()
return actual, loaded
}
// tryLoadOrStore(多协程操作,此时mu锁未锁定)
// 若该条目未被删除,原子地读取value值,loaded=true,ok=true
// 若该条目已被删除(expunged或nil),则tryLoadOrStore函数不修改,直接返回,loaded==false,ok==false
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
}
// nil的情况
ic := i
for {
// CAS操作,赋值
if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) {
return i, false, true
}
p = atomic.LoadPointer(&e.p)
if p == expunged {
return nil, false, false
}
if p != nil {
return *(*interface{})(p), true, true
}
}
}
// Delete deletes the value for a key.
func (m *Map) Delete(key interface{}) {
// 优先从read中读取
read, _ := m.read.Load().(readOnly)
e, ok := read.m[key]
if !ok && read.amended {
m.mu.Lock()
// 双重检测,因为加锁过程中dirty可能升为read,且dirty被置为nil
read, _ = m.read.Load().(readOnly)
e, ok = read.m[key]
// read不存在,但dirty存在
if !ok && read.amended {
// 锁定中,可以直接删
delete(m.dirty, key)
}
m.mu.Unlock()
}
// 存在时一定存在于read中,只需在entry标记为nil
if ok {
e.delete()
}
}
// 标记一个entry为删除(mu锁未加锁,需考虑多协程安全,本entry此时存在于read中)
func (e *entry) delete() (hadValue bool) {
for {
// 原子操作,重新读
p := atomic.LoadPointer(&e.p)
// nil 表已被其他协程通过Delete()标记删除,不再操作
// expunged 表已被其他协程提升dirty为read时,标记删除,不再操作
if p == nil || p == expunged {
return false
}
// CAS操作,置为nil
if atomic.CompareAndSwapPointer(&e.p, p, nil) {
return true
}
}
}
// 遍历,基于f函数
func (m *Map) Range(f func(key, value interface{}) bool) {
// 从read中获取
read, _ := m.read.Load().(readOnly)
// 已修改过,从dirty中获取
if read.amended {
m.mu.Lock()
read, _ = m.read.Load().(readOnly)
if read.amended {
read = readOnly{m: m.dirty}
m.read.Store(read)
m.dirty = nil
m.misses = 0
}
m.mu.Unlock()
}
for k, e := range read.m {
v, ok := e.load()
// 该值不存在了
if !ok {
continue
}
// 调用失败,终止
if !f(k, v) {
break
}
}
}
// 更新misses数,misseds >= len(dirty)时更新read\dirty
func (m *Map) missLocked() {
m.misses++
if m.misses < len(m.dirty) {
return
}
m.read.Store(readOnly{m: m.dirty})
m.dirty = nil
m.misses = 0
}
// 更新dirty
func (m *Map) dirtyLocked() {
if m.dirty != nil {
return
}
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
}
}
}
// 原子操作,判断entry的指针是否为nil(成功修改为expunged)
func (e *entry) tryExpungeLocked() (isExpunged bool) {
p := atomic.LoadPointer(&e.p)
for p == nil {
if atomic.CompareAndSwapPointer(&e.p, nil, expunged) {
return true
}
p = atomic.LoadPointer(&e.p)
}
return p == expunged
}
go sync.Map源码解读
最新推荐文章于 2023-10-17 18:39:03 发布