通过替换map指针的方式来更新map是否线程安全的?
1. 运行环境
系统: mac os 11.5.2
go版本:1.18.1
2. 问题描述?
问题背景:在线上环境中,有一种常见的场景为:服务启动时需要加载一些数据到内存中,并通过定时更新整体数据的方式供服务使用。这种场景下,通常用到的数据结构为map。
问题内容:
在stackoverflow.com/questions/347503... 中说应该加锁;在go里没有明确说这样做是线程安全的;但是我无论是线上环境还是下面的示例代码都没有遇到panic。所以我就很疑惑:在go里,整体更新map数据的时候,通过 直接替换map指针的方式而不是加锁 是否是线程安全的?这样整体更新map是否合适
以下为示例代码:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
mapShow := map[int]bool{}
for i := 0; i < 1000; i++ {
mapShow[i] = true
}
rand.Seed(time.Now().UnixNano())
go func() {
for i := 1; ; i++ {
time.Sleep(time.Millisecond * 500)
tmp := map[int]bool{}
for k := i * 100; k < (i+1)*100; k++ {
tmp[k] = true
}
mapShow = tmp
fmt.Println(i, "data change success")
}
}()
for i := 0; i < 500; i++ {
go func() {
for {
k := rand.Intn(2000)
if _, ok := mapShow[k]; ok {
fmt.Println(k, "success")
} else {
fmt.Println(k, "not found")
}
}
}()
}
select {}
}
3. 您期望得到的结果?
期望遇到panic或者数据不一致情况
4. 您实际得到的结果?
通过上述示例代码跑了2G数据没有遇到异常
我的理解是,golang中map并发读是没问题的,并发写会出现panic,你的代码中使用了单个协程赋值替换了原来的map(此处使用指针替换),并没有执行map的写入逻辑,不会出现并发写问题,且如果使用了多个协程执行替换map的操作,也不会出现panic,但会出现数据不一致的情况。参考:多个协程,没有使用任何互斥状态,对同一个int变量进行加1,程序运行没问题,但每次运行结果会不一致。 go.dev/doc/faq#atomic_maps # Why are map operations not defined to be atomic?