赞
踩
使用锁意味着要把一些并发操作串行化,实际上是降低了程序性能,特别是在多核CPU上;所以常说,能使用原子操作就不要用锁,不过原子操作只能对一些基本的数据类型提供支持,所以很有局限性。sync.Map对键的类型要求和map一样,不可以是切片,字典,函数,必须是可以判等的类型。由于这些键值的实际类型只有在程序运行期间才能确定,所以Go语言编译器是无法在编译期检查,不正确的实际类型肯定会在运行时panic。把针对同一个sync.Map的操作集中起来,统一编写检查代码,除此之外,把sync.Map封装在一个结构体中,往往是一个好选择。reflect.TypeOf(a).Comparable()得到a变量的类型是否是可比较的。其中一个map放在read字段里,read字段是atomic.Value,往这个atomic.Value里存储的是readOnly结构体,这个结构体有个字段是map。我们称这个map是只读字典,它不会增减键,但是可以修改键的值。atomic.Value的Store和Load方法是原子性的。unsafe.Pointer可以使用除了add之外的原子操作,比如load和store。dirty字段,它存储键值对的方式和read字段(readOnly)中的原生字典一致,同样是把值做转换和封装后进行存储的,称为脏字典。如果脏字典和只读字典都存在同一个键值对,那么这里的两个键指的肯定是同一个基本值,对于两个值来说也是如此?这两个字典在存储键和值时只会存入它们的某个指针,而不是基本值。type Map struct { mu Mutex read atomic.Value dirty map[interface{}]*entry misses int } type readOnly struct { m map[interface{}]*entry amended bool } var expunged = unsafe.Pointer(new(interface{})) type entry struct { p unsafe.Pointer // *interface{} } func newEntry(i interface{}) *entry { return &entry{p: unsafe.Pointer(&i)} }
为什么要加锁后再重新获取呢,涉及到双重判断的问题,因为可能进到if代码段后,之前的状态修改了。重新判断之后,如果可能脏字典里面有,就去脏字典获取。如果还没有获取到,那么返回nil,否则再进行原子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 { m.mu.Lock() // Avoid reporting a spurious miss if m.dirty got promoted while we were // blocked on m.mu. (If further loads of the same key will not miss, it's // not worth copying the dirty map for this key.) read, _ = m.read.Load().(readOnly) e, ok = read.m[key] if !ok && read.amended { e, ok = m.dirty[key] // Regardless of whether the entry was present, record a miss: this key // will take the slow path until the dirty map is promoted to the read // map. m.missLocked() } m.mu.Unlock() } if !ok { return nil, false } return e.load() } 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 } // 如果在只读字典中未命中的次数和脏字典长度相同,那么就把脏字典刷到只读字典中,然后脏字典设置为nil并重新计数 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 }
func (m *Map) Store(key, value interface{}) { read, _ := m.read.Load().(readOnly) 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 { 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. m.dirtyLocked() m.read.Store(readOnly{m: read.m, amended: true}) } m.dirty[key] = newEntry(value) } m.mu.Unlock() } // tryStore stores a value if the entry has not been expunged. // // If the entry is expunged, tryStore returns false and leaves the entry // unchanged. 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 } } }
// 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 { m.mu.Lock() read, _ = m.read.Load().(readOnly) e, ok = read.m[key] if !ok && read.amended { delete(m.dirty, key) } m.mu.Unlock() } if ok { e.delete() } } func (e *entry) delete() (hadValue bool) { for { p := atomic.LoadPointer(&e.p) if p == nil || p == expunged { return false } if atomic.CompareAndSwapPointer(&e.p, p, nil) { return true } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。