map 預設是並發不安全的,同時對 map 進行並發讀寫時,程序會 panic,原因如下:
Go 官方在經過了長時間的討論後,認為 Go map 更應適配典型使用場景(不需要從多個 goroutine 中進行安全訪問),而不是為了小部分情況(並發訪問),導致大部分程序付出加鎖代價(性能),決定了不支持。
場景: 2 個協程同時讀和寫,以下程序會出現致命錯誤:fatal error: concurrent map writes
package main
import (
"fmt"
"time"
)
func main() {
s := make(map[int]int)
for i := 0; i < 100; i++ {
go func(i int) {
s[i] = i
}(i)
}
for i := 0; i < 100; i++ {
go func(i int) {
fmt.Printf("map第%d個元素值是%d\n", i, s[i])
}(i)
}
time.Sleep(1 * time.Second)
}
如果想實現 map 線程安全,有兩種方式:
方式一:使用讀寫鎖 map + sync.RWMutex
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var lock sync.RWMutex
s := make(map[int]int)
for i := 0; i < 100; i++ {
go func(i int) {
lock.Lock()
s[i] = i
lock.Unlock()
}(i)
}
for i := 0; i < 100; i++ {
go func(i int) {
lock.RLock()
fmt.Printf("map第%d個元素值是%d\n", i, s[i])
lock.RUnlock()
}(i)
}
time.Sleep(1 * time.Second)
}
方式二:使用 Go 提供的 sync.Map
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var m sync.Map
for i := 0; i < 100; i++ {
go func(i int) {
m.Store(i, i)
}(i)
}
for i := 0; i < 100; i++ {
go func(i int) {
v, ok := m.Load(i)
fmt.Printf("Load: %v, %v\n", v, ok)
}(i)
}
time.Sleep(1 * time.Second)
}