package main
import (
"fmt"
"sync"
)
var maps = make(map[int]string,0)
func main() {
var wg sync.WaitGroup
wg.Add(4)
go write_call(&wg)
go write_calll(&wg)
go write_callll(&wg)
go read_call(&wg)
wg.Wait()
}
func read_call(wg *sync.WaitGroup) {
for{
fmt.Printf("输出:%+v\n",maps)
}
wg.Done()
}
func write_call(wg *sync.WaitGroup) {
for{
maps[1] = "aa"
fmt.Printf("写入\n")
}
wg.Done()
}
func write_calll(wg *sync.WaitGroup) {
for{
maps[2] = "bb"
fmt.Printf("写入\n")
}
wg.Done()
}
func write_callll(wg *sync.WaitGroup) {
for{
delete(maps,3)
//maps[3] = "cc"
fmt.Printf("删除\n")
}
wg.Done()
}
//我在测试多协程写入map的安全性,CPU两核。按照golang map的实现,多个协程写入同一个map应该报fatal error,或者多个写,一个删除也应该fatal error报错,我循环调用了几十万次,也没有报错。
golang map多协程写入、删除没有报fatal error
- 写回答
- 好问题 0 提建议
- 关注问题
- 邀请回答
-
3条回答 默认 最新
关注让【宝码香车】来帮您解答,本回答参考DeepSeek编写,并整理提供。
如果答案让您满意,请采纳、点赞、关注,非常感谢!
.
每日一句正能量,让它带给您好心情:生活的美好,在于它的不确定性,你永远不知道下一秒会有什么惊喜。
.根据你提供的代码和描述,我来解释为什么没有出现
fatal error。问题分析
你的代码确实存在并发读写map的安全问题,但Go的map并发检测机制并不是100%实时触发的。以下是主要原因:
1. 竞态条件检测的局限性
Go的map并发检测是基于内存访问冲突的,它只在检测到同时进行的读写操作时才会触发panic。由于你的操作是循环执行的,可能在某些时刻没有发生真正的冲突。
2. 操作模式的影响
在你的代码中:
- 三个写协程操作不同的key(1, 2, 3)
- 读协程只是读取整个map
- 删除操作针对不存在的key(3)
这种模式可能减少了真正的内存冲突概率。
3. CPU调度的影响
在两核CPU上,协程的调度相对稀疏,进一步降低了冲突的概率。
重现问题的方法
要可靠地重现并发写map的panic,可以使用以下改进的测试代码:
package main import ( "fmt" "sync" ) var maps = make(map[int]string, 0) var wg sync.WaitGroup func main() { // 增加协程数量,操作相同的key wg.Add(3) // 多个协程同时读写相同的key go concurrentWrite(1) go concurrentWrite(1) go concurrentRead(1) wg.Wait() } func concurrentWrite(key int) { defer wg.Done() for i := 0; i < 100000; i++ { maps[key] = fmt.Sprintf("value%d", i) // 频繁在写入和删除之间切换 if i%2 == 0 { delete(maps, key) } } } func concurrentRead(key int) { defer wg.Done() for i := 0; i < 100000; i++ { _ = maps[key] // 读取操作 } }正确的并发map使用方法
方法1:使用sync.Mutex
package main import ( "fmt" "sync" ) type SafeMap struct { m map[int]string mut sync.RWMutex } func NewSafeMap() *SafeMap { return &SafeMap{ m: make(map[int]string), } } func (s *SafeMap) Set(key int, value string) { s.mut.Lock() defer s.mut.Unlock() s.m[key] = value } func (s *SafeMap) Get(key int) (string, bool) { s.mut.RLock() defer s.mut.RUnlock() value, exists := s.m[key] return value, exists } func (s *SafeMap) Delete(key int) { s.mut.Lock() defer s.mut.Unlock() delete(s.m, key) }方法2:使用sync.Map(Go 1.9+)
package main import ( "fmt" "sync" ) func main() { var sm sync.Map var wg sync.WaitGroup wg.Add(4) go func() { defer wg.Done() for i := 0; i < 1000; i++ { sm.Store(1, "aa") } }() go func() { defer wg.Done() for i := 0; i < 1000; i++ { sm.Store(2, "bb") } }() go func() { defer wg.Done() for i := 0; i < 1000; i++ { sm.Delete(3) } }() go func() { defer wg.Done() for i := 0; i < 1000; i++ { if value, ok := sm.Load(1); ok { _ = value } } }() wg.Wait() }总结
- 你的代码确实存在并发安全问题,只是由于调度和操作模式的原因没有立即触发panic
- 不要依赖未定义行为,即使现在不panic,也不能保证在其他环境或条件下不出现问题
- 始终使用适当的同步机制来保护map的并发访问
建议使用
go run -race your_program.go来检测竞态条件,这会比运行时panic更早发现问题。本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报