map 多个协程写同一个key的值时资源竞争
一、map不是线程安全的。原因是其内部实现没有使用锁机制来保护对其的并发读写操作
多个goroutine同时更新map中的同一个键,可能会导致数据竞争和不确定的结果,甚至程序崩溃。
假设两个goroutine同时执行 myMap[“key”] = value1 和 myMap[“key”] = value2,
最终结果是不确定的,可能是 value1 被存储,也可能是 value2 被存储,或者出现更严重的数据损坏
//以下是一个可能导致数据竞争的错误示例:
func testMap() {
myMap := make(map[int]int)
var wg sync.WaitGroup
numGoroutines := 10
wg.Add(numGoroutines)
for i := 0; i < numGoroutines; i++ {
go func(n int) {
defer wg.Done()
myMap[n] = n * n
}(i)
}
wg.Wait()
fmt.Println(myMap)
}
二、解决办法
1、使用sync.Mutex
进行加锁保护:通过在读写操作前加锁,读写操作后解锁,确保同一时间只有一个goroutine
能操作map
2、使用sync.Map
:这是 Go 1.9 引入的并发安全的map
,内部使用了锁和原子操作来保证并发安全。
//使用sync.Mutex方案
func testMutexMap() {
myMap := make(map[int]int)
var wg sync.WaitGroup
var lock sync.Mutex
numGoroutines := 10
wg.Add(10)
for i := 0; i < numGoroutines; i++ {
go func(n int) {
defer wg.Done()
lock.Lock()
myMap[n] = n * n
lock.Unlock()
}(i)
}
wg.Wait()
fmt.Println(myMap)
}
//使用sync.map方案
func testSyncMap() {
var myMap sync.Map
var wg sync.WaitGroup
numGoroutines := 10
wg.Add(10)
for i := 0; i < numGoroutines; i++ {
go func(n int) {
defer wg.Done()
myMap.Store(n, n*n)
}(i)
}
wg.Wait()
myMap.Range(func(key, value any) bool {
fmt.Printf("Key: %v, Value: %v\n", key, value)
return true
})
}