Go map为什么是非线程安全的?

参考解析

map默认是并发不安全的,同时对map进行并发读写时,程序会panic,原因如下:

Go 官方在经过了长时间的讨论后,认为 Go map 更应适配典型使用场景(不需要从多个 goroutine 中进行安全访问),而不是为了小部分情况(并发访问),导致大部分程序付出加锁代价(性能),决定了不支持。

场景: 2个协程同时读和写,以下程序会出现致命错误:fatal error: concurrent map writes

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. func main() {
  7. s := make(map[int]int)
  8. for i := 0; i < 100; i++ {
  9. go func(i int) {
  10. s[i] = i
  11. }(i)
  12. }
  13. for i := 0; i < 100; i++ {
  14. go func(i int) {
  15. fmt.Printf("map第%d个元素值是%d
  16. ", i, s[i])
  17. }(i)
  18. }
  19. time.Sleep(1 * time.Second)
  20. }

如果想实现map线程安全,有两种方式:

方式一:使用读写锁 map + sync.RWMutex

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. )
  7. func main() {
  8. var lock sync.RWMutex
  9. s := make(map[int]int)
  10. for i := 0; i < 100; i++ {
  11. go func(i int) {
  12. lock.Lock()
  13. s[i] = i
  14. lock.Unlock()
  15. }(i)
  16. }
  17. for i := 0; i < 100; i++ {
  18. go func(i int) {
  19. lock.RLock()
  20. fmt.Printf("map第%d个元素值是%d
  21. ", i, s[i])
  22. lock.RUnlock()
  23. }(i)
  24. }
  25. time.Sleep(1 * time.Second)
  26. }

方式二:使用Go提供的 sync.Map

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. )
  7. func main() {
  8. var m sync.Map
  9. for i := 0; i < 100; i++ {
  10. go func(i int) {
  11. m.Store(i, i)
  12. }(i)
  13. }
  14. for i := 0; i < 100; i++ {
  15. go func(i int) {
  16. v, ok := m.Load(i)
  17. fmt.Printf("Load: %v, %v
  18. ", v, ok)
  19. }(i)
  20. }
  21. time.Sleep(1 * time.Second)