美文网首页
GO并发安全字典sync.map(1)

GO并发安全字典sync.map(1)

作者: 尼桑麻 | 来源:发表于2019-03-28 11:04 被阅读0次
sync.Map

一个并发安全的高级数据结构。这个字典类型提供了一些常用的键值存取操作方法,并保证了这些操作的并发安全。同时,它的存、取、删等操作都可以基本保证在常数时间内执行完毕。换句话说,它们的算法复杂度与map类型一样都是O(1)。于在2017年发布的Go 1.9中正式加入了并发安全的字典类型sync.Map

思考

设想,如果让你自己实现一个sync map,你回怎么做?

优化

与单纯使用原生map和互斥锁的方案相比,使用sync.Map可以显著地减少锁的争用。sync.Map本身虽然也用到了锁,但是,它其实在尽可能地避免使用锁。使用锁就意味着要把一些并发的操作强制串行化。这往往会降低程序的性能,尤其是在计算机拥有多个CPU核心的情况下。因此,我们常说,能用原子操作就不要用锁。

限制与要求

由于并发安全字典内部使用的存储介质正是原生字典,所以对原生字典的限制要求同样适用于sync.map.
键的实际类型不能是函数类型、字典类型和切片类型

使用建议

我们应该在每次操作并发安全字典的时候,都去显式地检查键值的实际类型。无论是存、取还是删,都应该如此。
更好的做法是,把针对同一个并发安全字典的这几种操作都集中起来,然后统一地编写检查代码。把并发安全字典封装在一个结构体类型中。
我们必须保证键的类型是可比较的(或者说可判等的)。如果你实在拿不准,那么可以先通过调用reflect.TypeOf函数得到一个键值对应的反射类型值(即:reflect.Type类型的值),然后再调用这个值的Comparable方法,得到确切的判断结果.

保证并发安全字典中的键和值的类型正确性方案

方案一:
type IntStrMap struct {
 m sync.Map
}

func (iMap *IntStrMap) Delete(key int) {
 iMap.m.Delete(key)
}

func (iMap *IntStrMap) Load(key int) (value string, ok bool) {
 v, ok := iMap.m.Load(key)
 if v != nil {
  value = v.(string)
 }
 return
}

func (iMap *IntStrMap) LoadOrStore(key int, value string) (actual string, loaded bool) {
 a, loaded := iMap.m.LoadOrStore(key, value)
 actual = a.(string)
 return
}

func (iMap *IntStrMap) Range(f func(key int, value string) bool) {
 f1 := func(key, value interface{}) bool {
  return f(key.(int), value.(string))
 }
 iMap.m.Range(f1)
}

func (iMap *IntStrMap) Store(key int, value string) {
 iMap.m.Store(key, value)
}

指定key的类型和value 值得类型,由编译器帮我们校验。
优点:是简单方便高效
缺点:灵活性差,如果你有多种key,value 的组合就得写很多雷同的代码。

方案二:
封装的结构体类型的所有方法,都可以与sync.Map类型的方法完全一致(包括方法名称和方法签名)。
在这些方法中,我们就需要添加一些做类型检查的代码了。另外,这样并发安全字典的键类型和值类型,必须在初始化的时候就完全确定。

type ConcurrentMap struct {
 m         sync.Map
 keyType   reflect.Type
 valueType reflect.Type
}

func (cMap *ConcurrentMap) Load(key interface{}) (value interface{}, ok bool) {
 if reflect.TypeOf(key) != cMap.keyType {
  return
 }
 return cMap.m.Load(key)
}

func (cMap *ConcurrentMap) Store(key, value interface{}) {
 if reflect.TypeOf(key) != cMap.keyType {
  panic(fmt.Errorf("wrong key type: %v", reflect.TypeOf(key)))
 }
 if reflect.TypeOf(value) != cMap.valueType {
  panic(fmt.Errorf("wrong value type: %v", reflect.TypeOf(value)))
 }
 cMap.m.Store(key, value)
}

存的时候通过反射取到key和value类型,与指定类型不符直接panic。也可以返回个 ok bool 。如果你不想太粗暴。
取的时候通过反射取到key类型,与指定类型不符直接返回。
这种情况下必须先要保证键和值的类型是可比较的。

方案总结

第一种方案存在一个很明显的缺陷,那就是无法灵活地改变字典的键和值的类型。一旦需求出现多样化,编码的工作量就会随之而来。

第二种方案很好地弥补了这一缺陷,但是,那些反射操作或多或少都会降低程序的性能。我们往往需要根据实际的应用场景,通过严谨且一致的测试,来获得和比较程序的各项指标,并以此作为方案选择的重要依据之一。

相关文章

  • GO并发安全字典sync.map(1)

    sync.Map 一个并发安全的高级数据结构。这个字典类型提供了一些常用的键值存取操作方法,并保证了这些操作的并发...

  • Go语言——sync.Map详解

    Go语言——sync.Map详解 sync.Map是1.9才推荐的并发安全的map,除了互斥量以外,还运用了原子操...

  • GO并发安全字典sync.map(2)

    并发安全字典如何做到尽量避免使用锁? 只读字典 sync.Map类型在内部使用了大量的原子操作来存取键和值,并使用...

  • map与sync.Map

    Go 语言原生 map 并不是线程安全的,对它进行并发读写操作的时候,需要加锁。而 sync.map 则是一种并发...

  • Go sync.Map

    map并发读线程安全,并发读写线程不安全。 sync.Map 读写分离 空间换时间 Map Golang1.6之前...

  • go 中的 sync.Map

    sync.map sync.Map 的使用和内置的 map 类型一样,只不过它是并发安全的。 Store:存储一对...

  • Go语言高并发Map解决方案

    Go语言高并发Map解决方案 Go语言基础库中的map不是并发安全的,不过基于读写锁可以实现线程安全;不过在Go1...

  • 基于亿条数据的内存,redis两种聚合GO计算的实战思考

    总结: (1) go 的 map读,写是线程不安全的(2) sync.map 用空间换时间的思想,适合读多写少的场...

  • 构建一个线程安全的字典

    线程安全锁字典ConcurrentDictionary(并发字典) ConcurrentDictionary的写操...

  • 2017年5月17日晨读

    1.go package 的规范指南2.go structs 的并发安全3.go tcp详解4.go tool t...

网友评论

      本文标题:GO并发安全字典sync.map(1)

      本文链接:https://www.haomeiwen.com/subject/ywmfbqtx.html