2种情况:
- 并发操作时:不可以。map在读的时候会检测写标志,如果发现一个协程在读的时候,另一个协程在写(包括删除和插入),则引发
fatal error: concurrent map iteration and map write
- 单个routine:可以,但是不建议。因为遍历的结果中可能包含删除的 key,也可能不包含,这取决于删除 key 的时间:是在当前遍历 key 所在的 bucket 前还是后,可能会引发意外的逻辑错误。
并发读写(以插入和遍历为例)的场景我们测试一下:
package main
import (
"fmt"
"strconv"
"time"
)
func main() {
m := make(map[int]string, 200)
go func() {
for {
for i := 0; i < 1000; i++ {
m[i] = strconv.Itoa(i)
}
time.Sleep(time.Microsecond)
}
}()
go func() {
for {
count := 0
for range m {
count++
time.Sleep(time.Microsecond)
}
fmt.Println("map count:", count)
time.Sleep(time.Second)
}
}()
time.Sleep(1 * time.Minute)
}
执行:
fatal error: concurrent map iteration and map write
goroutine 35 [running]:
main.main.func2()
15_go_syntax/maps/hash_map.go:30 +0xc7
created by main.main
15_go_syntax/maps/hash_map.go:27 +0xaa
goroutine 1 [sleep]:
time.Sleep(0xdf8475800)
/usr/local/Cellar/go/1.20.4/libexec/src/runtime/time.go:195 +0x135
main.main()
15_go_syntax/maps/hash_map.go:39 +0xb9
goroutine 34 [runnable]:
main.main.func1()
15_go_syntax/maps/hash_map.go:21 +0x7a
created by main.main
15_go_syntax/maps/hash_map.go:18 +0x6d