Golang使用Map的正确姿势-演道网

初始化后再使用

Golang中,map是引用类型,如切片一样,通过下面的代码声明后指向的是nil,所以千万别直接声明后就使用,新手可能经常会犯如下错误:

  1. var m map[string]string

  2. m["result"] = "result"

由于字典是引用类型,所以当我们仅声明而不初始化一个字典类型的变量的时候,他的值是nil。对值为nil的字段除添加键值对外其他操作都不会引发错误。上面的第一行代码对其进行写入操作,就是对空指针的引用,这将会造成一个painc。所以,得记得用 make函数对其进行分配内存和初始化:

  1. m := make(map[string]string)

并发安全

并发安全也叫线程安全,在并发中出现了数据的丢失,称为并发不安全我们都知道非原子操作的都不是并发安全的,在Golang中map,其读写操作并不保证并发安全。如下面的操作

  1. c := make(map[string]string)

  2. wg := sync.WaitGroup{}

  3. for i := 0; i < 10; i++ {

  4. wg.Add(1)

  5. go func(n int) {

  6. k, v := strconv.Itoa(n), strconv.Itoa(n)

  7. c[k] = v

  8. wg.Done()

  9. }(i)

  10. }

  11. wg.Wait()

  12. fmt.Println(c)

运行则会出现下面的错误

  1. fatal error: concurrent map writes

你可以通过运行 go run--race来对程序进行竞态检测

那么如何解决这个问题呢?

通过锁机制解决并发问题

下面介绍几种常见的并发安全的操作map的方法

sync.Mutex

  1. c := make(map[string]string)

  2. wg := sync.WaitGroup{}

  3. var lock sync.Mutex

  4. for i := 0; i < 10; i++ {

  5. wg.Add(1)

  6. go func(n int) {

  7. k, v := strconv.Itoa(n), strconv.Itoa(n)

  8. lock.Lock()

  9. c[k] = v

  10. lock.Unlock()

  11. wg.Done()

  12. }(i)

  13. }

  14. wg.Wait()

  15. fmt.Println(c)

当然这里也可以使用读写锁 sync.RWMutex,这样并发读多的场景下性能要好。

sync.Map

sync.Map 是官方出品的并发安全的map,他在内部使用了大量的原子操作来存取键和值,并使用了read和dirty二个原生map作为存储介质,具体实现流程可阅读相关源码。

  1. var m sync.Map


  2. //写

  3. m.Store("foo", "hello world")

  4. m.Store("slice", []int{1, 2, 3, 4, 5, 6, 7})

  5. m.Store("int", 123)

  6. //读

  7. m.Load("foo")

  8. m.Load("slice")

  9. m.Load("int")

  10. //删

  11. m.Delete("int")

第三方map包

第三方包的实现都大同小异,基本上都是使用分离锁来实现并发安全的,具体分离锁来实现并发安全的原理可参考下面的延伸阅读

concurrent-map

  1. m := cmap.New()

  2. //写

  3. m.Set("foo", "hello world")

  4. m.Set("slice", []int{1, 2, 3, 4, 5, 6, 7})

  5. m.Set("int", 1)

  6. //读

  7. m.Get("foo")

  8. m.Get("slice")

  9. m.Get("int")

go-concurrentMap

  1. m := concurrent.NewConcurrentMap()

  2. m.Put("foo", "hello world")

  3. m.Put("slice", []int{1, 2, 3, 4, 5, 6, 7})

  4. m.Put("int", 1)

  5. //读

  6. m.Get("foo")

  7. m.Get("slice")

  8. m.Get("int")

延伸阅读

HashMap原理和实现 https://www.jianshu.com/p/82f344d58917

探索 ConcurrentHashMap 高并发性的实现机制 https://www.ibm.com/developerworks/cn/java/java-lo-concurrenthashmap/index.html

转载自演道,想查看更及时的互联网产品技术热点文章请点击http://go2live.cn