showliuzp 2025-11-07 12:56 采纳率: 84.3%
浏览 4
已结题

golang map并发读写加速仍旧报错


package main

import (
    "fmt"
    "sync"
)

var maps = make(map[int]string,0)

func main() {
    var wg sync.WaitGroup
    wg.Add(2)

    go write_call(1,&wg)
    go concurrentRead(1,&wg)
    wg.Wait()
}

func write_call(key int,wg *sync.WaitGroup) {
    i :=0
    var mu sync.Mutex
    for{
        mu.Lock()
        maps[i] = fmt.Sprintf("value%d", i)
        mu.Unlock()
        i++
    }
        wg.Done()
}

func concurrentRead(key int,wg *sync.WaitGroup) {
    i:=0
    var mu sync.Mutex
    for{
        mu.Lock()
        val := maps[i] // 读取操作
        fmt.Printf("读取数据:%+v\n",val)
        mu.Unlock()
        i++
    }
}


//我在测试两个协程对一个maps进行读写是否有冲突的,我在读和写的协程里对maps的操作都加锁了,可还是报fatal error: concurrent map read and map write
  • 写回答

3条回答 默认 最新

  • 宝码香车 优质创作者: JavaScript技术领域 2025-11-07 16:36
    关注

    让【宝码香车】来帮您解答,本回答参考DeepSeek编写,并整理提供。

    如果答案能帮助您,请采纳、点赞,非常感谢!
    .
    每日一句正能量,让它带给您好心情:别让懒惰吞噬你的梦想,行动起来,一切都还来得及。
    .

    看到你的代码,我发现了问题所在。虽然你在读写操作中都加了锁,但每个协程使用的是自己独立的互斥锁,这完全失去了锁的保护作用。

    问题分析

    在你的代码中:

    • write_call 函数创建了自己的 sync.Mutex
    • concurrentRead 函数也创建了自己的 sync.Mutex
    • 两个锁是独立的,无法实现互斥访问

    解决方案

    方案1:使用全局互斥锁(推荐)

    package main
    
    
    import (
        "fmt"
        "sync"
    )
    
    
    var maps = make(map[int]string, 0)
    var mu sync.Mutex  // 全局互斥锁
    
    
    func main() {
        var wg sync.WaitGroup
        wg.Add(2)
    
    
        go write_call(1, &wg)
        go concurrentRead(1, &wg)
        wg.Wait()
    }
    
    
    func write_call(key int, wg *sync.WaitGroup) {
        defer wg.Done()
        i := 0
        for {
            mu.Lock()
            maps[i] = fmt.Sprintf("value%d", i)
            mu.Unlock()
            i++
        }
    }
    
    
    func concurrentRead(key int, wg *sync.WaitGroup) {
        defer wg.Done()
        i := 0
        for {
            mu.Lock()
            val := maps[i] // 读取操作
            fmt.Printf("读取数据:%+v\n", val)
            mu.Unlock()
            i++
        }
    }
    

    方案2:使用读写锁(更高效)

    对于读多写少的场景,使用 sync.RWMutex 性能更好:

    package main
    
    
    import (
        "fmt"
        "sync"
    )
    
    
    var maps = make(map[int]string, 0)
    var rwMu sync.RWMutex  // 全局读写锁
    
    
    func main() {
        var wg sync.WaitGroup
        wg.Add(2)
    
    
        go write_call(1, &wg)
        go concurrentRead(1, &wg)
        wg.Wait()
    }
    
    
    func write_call(key int, wg *sync.WaitGroup) {
        defer wg.Done()
        i := 0
        for {
            rwMu.Lock()  // 写锁
            maps[i] = fmt.Sprintf("value%d", i)
            rwMu.Unlock()
            i++
        }
    }
    
    
    func concurrentRead(key int, wg *sync.WaitGroup) {
        defer wg.Done()
        i := 0
        for {
            rwMu.RLock()  // 读锁
            val := maps[i]
            fmt.Printf("读取数据:%+v\n", val)
            rwMu.RUnlock()
            i++
        }
    }
    

    方案3:使用 sync.Map(最简单)

    对于并发访问,Go 提供了专门的 sync.Map

    package main
    
    
    import (
        "fmt"
        "sync"
    )
    
    
    var syncMap sync.Map
    
    
    func main() {
        var wg sync.WaitGroup
        wg.Add(2)
    
    
        go write_call(1, &wg)
        go concurrentRead(1, &wg)
        wg.Wait()
    }
    
    
    func write_call(key int, wg *sync.WaitGroup) {
        defer wg.Done()
        i := 0
        for {
            syncMap.Store(i, fmt.Sprintf("value%d", i))
            i++
        }
    }
    
    
    func concurrentRead(key int, wg *sync.WaitGroup) {
        defer wg.Done()
        i := 0
        for {
            if val, ok := syncMap.Load(i); ok {
                fmt.Printf("读取数据:%+v\n", val)
            }
            i++
        }
    }
    

    关键要点

    1. 锁必须是共享的:所有需要互斥访问的协程必须使用同一个锁实例
    2. 注意死锁:确保在函数退出前释放锁(使用 defer
    3. 性能考虑:根据读写比例选择合适的同步机制
    4. 无限循环:你的代码中有无限循环,wg.Done() 永远不会执行,需要考虑退出机制

    选择哪种方案取决于你的具体需求,对于简单的并发访问,方案1或方案3都是不错的选择。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(2条)

报告相同问题?

问题事件

  • 系统已结题 11月15日
  • 已采纳回答 11月7日
  • 创建了问题 11月7日