Golang中,map是引用类型,如切片一样,通过下面的代码声明后指向的是nil,所以千万别直接声明后就使用,新手可能经常会犯如下错误:
var m map[string]string
m["result"] = "result"
make
m := make(map[string]string)
并发安全
并发安全也叫线程安全,在并发中出现了数据的丢失,称为并发不安全我们都知道非原子操作的都不是并发安全的,在Golang中map,其读写操作并不保证并发安全。如下面的操作
c := make(map[string]string)
wg := sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)
go func(n int) {
k, v := strconv.Itoa(n), strconv.Itoa(n)
c[k] = v
wg.Done()
}(i)
}
wg.Wait()
fmt.Println(c)
运行则会出现下面的错误
fatal error: concurrent map writes
go run --race
那么如何解决这个问题呢?
通过锁机制解决并发问题
下面介绍几种常见的并发安全的操作map的方法
sync.Mutex
c := make(map[string]string)
wg := sync.WaitGroup{}
var lock sync.Mutex
for i := 0; i < 10; i++ {
wg.Add(1)
go func(n int) {
k, v := strconv.Itoa(n), strconv.Itoa(n)
lock.Lock()
c[k] = v
lock.Unlock()
wg.Done()
}(i)
}
wg.Wait()
fmt.Println(c)
sync.RWMutex
sync.Map
sync.Map
var m sync.Map
//写
m.Store("foo", "hello world")
m.Store("slice", []int{1, 2, 3, 4, 5, 6, 7})
m.Store("int", 123)
//读
m.Load("foo")
m.Load("slice")
m.Load("int")
//删
m.Delete("int")
第三方map包
第三方包的实现都大同小异,基本上都是使用分离锁来实现并发安全的,具体分离锁来实现并发安全的原理可参考下面的延伸阅读
m := cmap.New()
//写
m.Set("foo", "hello world")
m.Set("slice", []int{1, 2, 3, 4, 5, 6, 7})
m.Set("int", 1)
//读
m.Get("foo")
m.Get("slice")
m.Get("int")
m := concurrent.NewConcurrentMap()
m.Put("foo", "hello world")
m.Put("slice", []int{1, 2, 3, 4, 5, 6, 7})
m.Put("int", 1)
//读
m.Get("foo")
m.Get("slice")
m.Get("int")
延伸阅读
本文亦在微信公众号【小道资讯】发布,欢迎扫码关注!