V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
beego
zemul
V2EX  ›  Go 编程语言

golang map 并发读写竞争问题

  •  
  •   zemul · 196 天前 · 1695 次点击
    这是一个创建于 196 天前的主题,其中的信息可能已经有所发展或是发生改变。
    type IdService struct {
     area  int64
     node  int64
     apply map[string]*apply
    }
    

    map 并发读写会产生数据竞争,但如果 value 是一个指针,只修改指针对象内的元素,还会有数据竞争问题吗?

    11 条回复    2021-11-02 21:00:24 +08:00
    sdrzlyz
        1
    sdrzlyz  
       196 天前
    不会引发 panic ,但是这个 value 对应的数据,其它 goroutine 读到的不一定是最新的。

    map 本身又没有发生增减的情况,这种场景下,你用 map 图了个啥?
    JKeita
        2
    JKeita  
       196 天前
    不会,不关心数据修改顺序性就没啥影响
    sunny352787
        3
    sunny352787  
       196 天前
    牵扯到多线程就用 sync.Map ,不要自己处理,手动加锁的话性能也会慢很多
    bruce0
        4
    bruce0  
       196 天前
    @sunny352787 sync.Map 这个适用于读多写少的情况,如果是写多读少的话 自己加锁 可能会更高。当然,绝大多数并发 情况下, 无脑 sync.map 就好了
    rrfeng
        5
    rrfeng  
       196 天前
    你这个 map 并没有写操作,所以没问题。
    xmge
        6
    xmge  
       196 天前
    go map 的并发读写不会 panic ,而是直接调用 throw() 函数,导致程序退出,因此,如果程序中有可能出现 map 并发读写的情况,一定要处理掉,因为这种错误出现时,程序必然挂掉。

    上述的结构是会出现问题的,map 并发读写的检查大概是:当读取某个 key 时会判断一个是否有协程在写的变量,如果有协程在写,则程序退出。

    测试代码:

    ```go
    package main

    import "sync"

    type Person struct {
    Name string
    }

    func main() {
    m := make(map[string]*Person)
    wg := sync.WaitGroup{}
    for i := 0; i < 100; i++ {
    wg.Add(1)
    go func() {
    defer wg.Done()
    for i := 0; i < 1000; i++ {
    m["1"] = new(Person)
    }
    }()
    }
    wg.Wait()
    }
    ```

    报错信息:

    ```
    fatal error: concurrent map writes

    goroutine 21 [running]:
    runtime.throw(0x1076c2d, 0x15)
    /usr/local/go/src/runtime/panic.go:1117 +0x72 fp=0xc00002ff08 sp=0xc00002fed8 pc=0x102dd12
    runtime.mapassign_faststr(0x1068c80, 0xc000098000, 0x1075205, 0x1, 0xc000056088)
    /usr/local/go/src/runtime/map_faststr.go:211 +0x3f1 fp=0xc00002ff70 sp=0xc00002ff08 pc=0x100ea91
    main.main.func1(0xc00009a000, 0xc000098000)
    /Users/maning/go/tmp/hex.go:17 +0xac fp=0xc00002ffd0 sp=0xc00002ff70 pc=0x105f3ec
    runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:1371 +0x1 fp=0xc00002ffd8 sp=0xc00002ffd0 pc=0x105bb81
    created by main.main
    /Users/maning/go/tmp/hex.go:14 +0x91

    ```
    xmge
        7
    xmge  
       196 天前
    看错题了!尴尬
    stach
        8
    stach  
       196 天前
    只修改指针对象内的元素,不会有数据竞争问题 (并发读写竞争), 会有数据错误问题 (并发安全问题).
    前者着重于程序是否可以正常 run, 后者着重于数据是否可以准确的在多线程中 share.

    只要是在 多线程, 读写场景, 就要考虑加锁, 或者采用原子操作等方式, 来进行 线程间 同步.
    bazingaterry
        9
    bazingaterry  
       196 天前
    「修改指针对象内的元素」这里的 map 不存在 data race ,但其他地方不好说。例如指针并发读写请用 https://pkg.go.dev/sync/atomic#Value ,其他拿不准的情况把 https://golang.org/doc/articles/race_detector 打开看看。
    DCjanus
        10
    DCjanus  
       195 天前 via Android
    只修改指针对象内的元素相当于多线程持有一个指针,并一起修改对应对象,每次修改不是原子的话,可能读到的是中间状态,即使修改是原子的,也有可能读到非预期的值。
    zemul
        11
    zemul  
    OP
       195 天前
    了解了,感谢解答!
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1117 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 21:08 · PVG 05:08 · LAX 14:08 · JFK 17:08
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.