The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
yujianwjj

go map 并发写的问题

  •  
  •   yujianwjj · Jan 25, 2021 · 6659 views
    This topic created in 1947 days ago, the information mentioned may be changed or developed.

    场景是多个 goroutine 对一个 map 只写不读。

    最开始用加锁的方式,来实现多个 goroutine 对一个 map 进行写入。后来发现效率有点低。就尝试了下不加锁的方式。

    func TestMap(t *testing.T) {
    	a := map[string]int{}
    	count := 100
    	wg := sync.WaitGroup{}
    	wg.Add(count)
    	for i := 0; i < count; i++ {
    		go func(i int) {
    			a[fmt.Sprintf("%d", i)] = i
    			wg.Done()
    		}(i)
    	}
    	wg.Wait()
    	for k, v := range a {
    		fmt.Println(k, " ", v)
    	}
    }
    

    以上测试代码能够正常工作,并且写入的数据正确,我就以为 map 只写不读的情况是可以不加锁的。

    但是实际场景中 count 为 5000,然后就报错了:

    fatal error: concurrent map writes
    

    现在有两个问题:

    1. 为什么 count 为 5000 就报错,100 的时候不报错。
    2. 多个 goroutine 对 map 只写不读的场景有什么效率更高的方式。
    30 replies    2021-01-27 14:14:22 +08:00
    YUX
        1
    YUX  
    PRO
       Jan 25, 2021
    用 sync.Map
    Takamine
        2
    Takamine  
       Jan 25, 2021 via Android
    5000 报错,100 不报错就是单纯概率问题吧。

    只写不读可以给 map 包一层方法,在写的地方加锁。
    BeautifulSoap
        3
    BeautifulSoap  
       Jan 25, 2021 via Android
    因为你 count 5000 的时候触发 map 同时写入的几率非常高啊。。。

    100 一下子就执行完毕了数量也少同时写入几率小
    kiddingU
        4
    kiddingU  
       Jan 25, 2021   ❤️ 1
    @YUX 看楼主的场景是只写不读,sync.Map 不适合这种场景,锁的粒度太大, 用 concurrent map 就行,或者自己写一个算法,减小锁的粒度
    MidGap
        5
    MidGap  
       Jan 25, 2021   ❤️ 1
    哈哈哈哈哈好可爱
    JKeita
        6
    JKeita  
       Jan 25, 2021
    我这 10 都报错,可能跟电脑 CPU 性能有关吧,把数据先并发入 chan,再按顺序读取写入 map ?
    monsterxx03
        7
    monsterxx03  
       Jan 25, 2021
    100 的时候你多试几次就挂了,或者加 -race.
    sync.Map 只对读多写少的场景有效率提升.
    单 map 每次写加锁可能还不如顺序写.
    一般优化思路是做 sharding, 比如预先分配 8 个 map, 每次写的时候 i%8 决定写入哪个 map
    capti0n
        8
    capti0n  
       Jan 25, 2021
    个人理解:
    1.golang 的 map 是 hashmap,会默认分配一部分桶出来,这时并行写入或者访问没问题,
    当超过一定阈值,会触发扩容,这时就会有并发问题。
    2.sync.map 有试过吗?
    yujianwjj
        9
    yujianwjj  
    OP
       Jan 25, 2021
    @kiddingU 你说的 concurrent map 是这个吗? https://github.com/orcaman/concurrent-map
    Jooooooooo
        10
    Jooooooooo  
       Jan 25, 2021
    第一个疑问再次说明并发 bug 很难发现.
    kiddingU
        11
    kiddingU  
       Jan 25, 2021
    @yujianwjj 是的,也是加锁,只不过是对锁进行了 shard,减轻锁的粒度
    YUX
        12
    YUX  
    PRO
       Jan 25, 2021 via iPhone
    @kiddingU 好的 学习了
    cloverstd
        13
    cloverstd  
       Jan 25, 2021
    比较好奇😯,什么业务场景下是只写不读的
    如果只写,是不是可以考虑换个 free-lock 的数据结构来存
    joesonw
        14
    joesonw  
       Jan 25, 2021   ❤️ 1
    nuk
        15
    nuk  
       Jan 25, 2021
    因为加的越多 hash 冲突就越多,添加一个键花的时间就越久,超过了启动一个 goroutine 的时间,就会报错了。
    用无锁队列然后单线程写好一点吧,写 map 应该不是瓶颈
    ihipop
        16
    ihipop  
       Jan 25, 2021 via Android
    @nuk 弄个 chanel 单向灌进去就行吧。。
    nuk
        17
    nuk  
       Jan 25, 2021
    @ihipop channel 有锁的呀,太多 goroutine 写就不行了
    YouLMAO
        18
    YouLMAO  
       Jan 25, 2021
    楼主是只写不读, 必须用 mutex, 连 rwmutex 都不要, 必须比 sync.Map 性能好, 我说的, 性能经过 G 家认证
    raaaaaar
        19
    raaaaaar  
       Jan 25, 2021 via Android
    为什么只写不读?只写的的话那些数据有什么用,是什么业务场景啊
    felixin
        20
    felixin  
       Jan 25, 2021 via Android
    If there is only one lesson I learn from the 30 years experience of network/multi-threading programming, that is NEVER SHARE STATES.

    Pieter Hintjens
    xmge
        21
    xmge  
       Jan 25, 2021
    1. 概率问题,对共享资源同时操作肯定会报错
    2. 如果是只读不写,只能加锁,sync.map 也不要用,sync.map 底层是读写分离,写时加锁。
    yzbythesea
        22
    yzbythesea  
       Jan 26, 2021
    chan or mutex lock
    Kinnice
        23
    Kinnice  
       Jan 26, 2021
    你电脑性能有点好
    yujianwjj
        24
    yujianwjj  
    OP
       Jan 26, 2021
    抱歉,题目描述有误,我的场景是先写后读,先加载大量的数据到 map 里面,后面再查找。
    march1993
        25
    march1993  
       Jan 26, 2021
    用 goroutine+chan 啊,一个 routine 专门读 chan 然后修改 map,其他 routine 把要修改的内容发到 chan 里
    sunshinev
        26
    sunshinev  
       Jan 26, 2021
    sync.Map
    mengdodo
        27
    mengdodo  
       Jan 26, 2021
    我记得当初看到过这样一句话:Go 中的 Map 类型不是一种安全的数据类型。所以我比较菜,直接写到 redis 中
    kiddingU
        28
    kiddingU  
       Jan 26, 2021
    说用 chan 的,chan 底层数据结构是个啥,有研究过吗~
    Dongxiem
        29
    Dongxiem  
       Jan 27, 2021
    @kiddingU 这个很难说的清楚的吧?可以看看这个 深度解密 Go 语言之 channel ( https://zhuanlan.zhihu.com/p/74613114
    kiddingU
        30
    kiddingU  
       Jan 27, 2021
    有啥难说清楚的,看源码不就清楚了~
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   3785 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 75ms · UTC 04:19 · PVG 12:19 · LAX 21:19 · JFK 00:19
    ♥ Do have faith in what you're doing.