- go语言早期的Map并不是并发安全的,在1.9版本才加入的sync.Map
- sync.Map的键和值都是interface{},所以可以存储任何类型的数据。不过与Map一样,键的类型不能是函数,字典和切片类型,这是因为程序需要在编译期间对键值进行检查,不正确的键值类型会panic
- 在日常使用时可以对sync.Map进行包装,显示的指定map的键类型。
func (strmap *StrSyncMap)load(key string)(value interface{},ok bool) {
load, ok := strmap.m.Load(key)
if load != nil {
value = load.(string)
}
return
}
func (strMap *StrSyncMap)Store( key string, value interface{}) {
strMap.m.Store(key,value)
}
func (strMap *StrSyncMap)LoadOrStore(key string,value interface{}) (actual interface{},loaded bool) {
store, loaded := strMap.m.LoadOrStore(key, value)
if store != nil {
actual = store.(string)
}
return
}
func (strMap *StrSyncMap)Range(f func(key string,value interface{}) bool) {
f1:= func(key , value interface{}) bool {
return f(key.(string),value)
}
strMap.m.Range(f1)
}
func (strMap *StrSyncMap)delete(key string) {
strMap.m.Delete(key)
}
type CustomMap struct{
map sync.Map
keyType reflect.Type
value reflect.Type
}
func NewCustomMap(keyType, valueType reflect.Type) (*CustomMap, error) {
if keyType == nil {
return nil, errors.New("nil key type")
}
if !keyType.Comparable() {
return nil, fmt.Errorf("incomparable key type: %s", keyType)
}
if valueType == nil {
return nil, errors.New("nil value type")
}
cMap := &CustomMap{
keyType: keyType,
valueType: valueType,
}
return cMap, nil
}
//在查找和使用的时候判断类型即可。
- sync.Map解析
- sync.Map在内部使用了大量的原子操作来存储数据,最大程度减少锁都使用。
- sync.Map内部有一个read字段,是atomic.Value类型,也就是interface{}类型。它相当于一个快照,在条件满足时,会保存sync.Map中的所有数据,不会增减,但会被允许修改键所对应的值。它先把值转换为了unsafe.Pointer类型的值,然后再把后者封装,并储存在其中的原生字典中。如此一来,在变更某个键所对应的值的时候,就也可以使用原子操作了。
- sync.Map中的另一个原生字典由它的dirty字段代表。 它存储键值对的方式与read字段中的原生字典一致,它的键类型也是interface{},并且同样是把值先做转换和封装后再进行储存的。
- sync.Map在查找指定的键所对应的值的时候,总会先去只读字典中寻找,并不需要锁定互斥锁。只有当确定“只读字典中没有,但脏字典中可能会有这个键”的时候,它才会在锁的保护下去访问脏字典。相对应的,sync.Map在存储键值对的时候,只要只读字典中已存有这个键,并且该键值对未被标记为“已删除”,就会把新值存到里面并直接返回,这种情况下也不需要用到锁。如果没有会在锁的保护下将键值存储到脏字典当中。
- 只读字典和脏字典之间是会互相转换的。在脏字典中查找键值对次数足够多的时候,sync.Map会把脏字典直接作为只读字典,保存在它的read字段中,然后把代表脏字典的dirty字段的值置为nil。