这个包很常用, 主要是用于在并发情况下的map...
// Map is like a Go map[interface{}]interface{} but is safe for concurrent use
// by multiple goroutines without additional locking or coordination.
官方的解释 很到位嘛 ...
// The Map type is optimized for two common use cases: (1) when the entry for a given
// key is only ever written once but read many times, as in caches that only grow,
// or (2) when multiple goroutines read, write, and overwrite entries for disjoint
// sets of keys. In these two cases, use of a Map may significantly reduce lock
// contention compared to a Go map paired with a separate Mutex or RWMutex.
翻译一下... map对两种常见的情况进行了优化, 下面这两种情况下 显著提高性能... 牛皮的呢.
- 一写多读, 例如cache...
- 并发读, 写, 覆写不想交的key集合.
type Map struct {
mu Mutex // 锁.
read atomic.Value //?
dirty map[interface{}]*entry
misses
}
对外暴露的方法
func (m *Map) Load(key interface{}) (value interface{}, ok bool) //读
func (m *Map) Store(key, value interface{}) // 写
func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) // 存在则读, 不存在则写了之后读, 有点像优化cache的感觉.
func (m *Map) Delete(key interface{}) // 删除
func (m *Map) Range(f func(key, value interface{}) bool) // 遍历Map中所有key.
大概的用法.
package main
import (
"sync"
"fmt"
)
func main() {
var mTest sync.Map
// fmt.Println(mTest)
// 读, 写, 读, 覆写, 读
fmt.Println(mTest.Load("A"))
mTest.Store("A", "a")
fmt.Println(mTest.Load("A"))
mTest.Store("A", "aa")
fmt.Println(mTest.Load("A"))
// 读或者写
fmt.Println(mTest.LoadOrStore("A", "aa"))
fmt.Println(mTest.LoadOrStore("B", "b"))
fmt.Println(mTest.LoadOrStore("B", "bb"))
// 删,读
mTest.Delete("A")
fmt.Println(mTest.Load("A"))
mTest.Delete("AA")
fmt.Println(mTest.Load("AA"))
// 遍历
mTest.Range(func(key, value interface{}) bool {
fmt.Println(key, value)
return true
})
}
源码分析一下 Map.Store 参考这篇文章, 这个有点牛皮了.
https://www.jianshu.com/p/ec51dac3c65b
func (m *Map) Store(key, value interface{}) {
read, _ := m.read.Load().(readOnly) // 原子读然后类型断言.
if e, ok := read.m[key]; ok && e.tryStore(&value) { // key存在就 试着存一下...
return // cas 存入成功就完事大吉了 . 没存成功说明 key 已经被标记删除了 .
}
m.mu.Lock()
read, _ = m.read.Load().(readOnly) // 加锁之后 重新读一遍, 防止这中间有改变, 上面那次读没加锁.
if e, ok := read.m[key]; ok { // key存在
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. // 说明dirty不为空... 将key写入dirty.?
m.dirty[key] = e // 去除成功的话就写入entry
}
e.storeLocked(&value) // 改变entry的值, 两个map同时生效.
} 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 的源码分析
func (e *entry) tryStore(i *interface{}) bool {
p := atomic.LoadPointer(&e.p) // 这里原子去读 防止读到脏数据.
if p == expunged { // expunged 是初始化时生产的一个地址 , 用于作为标记.
return false
}
for {
if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) { // 这里使用 CAS 方式 乐观锁.
return true
}
p = atomic.LoadPointer(&e.p) // 旧值改变 重新原子读一遍, 重新比较.
if p == expunged {
return false
}
}
}
// 这里用到了 atomic 库
// 这个函数的意思是, 如果 *add 等于传入的 old 值, 那么把 new 值赋值给 *add ...
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
网友评论