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

go map 并发写的问题

  •  
  •   yujianwjj · Jan 25, 2021 · 7199 views
    This topic created in 2115 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
    YUX
    Jan 25, 2021
    1
    用 sync.Map
    Takamine
    Takamine
    Jan 25, 2021 via Android
    2
    5000 报错,100 不报错就是单纯概率问题吧。

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

    100 一下子就执行完毕了数量也少同时写入几率小
    kiddingU
    kiddingU
    Jan 25, 2021❤️1
    4
    @YUX 看楼主的场景是只写不读,sync.Map 不适合这种场景,锁的粒度太大, 用 concurrent map 就行,或者自己写一个算法,减小锁的粒度
    MidGap
    MidGap
    Jan 25, 2021❤️1
    5
    哈哈哈哈哈好可爱
    JKeita
    JKeita
    Jan 25, 2021
    6
    我这 10 都报错,可能跟电脑 CPU 性能有关吧,把数据先并发入 chan,再按顺序读取写入 map ?
    monsterxx03
    monsterxx03
    Jan 25, 2021
    7
    100 的时候你多试几次就挂了,或者加 -race.
    sync.Map 只对读多写少的场景有效率提升.
    单 map 每次写加锁可能还不如顺序写.
    一般优化思路是做 sharding, 比如预先分配 8 个 map, 每次写的时候 i%8 决定写入哪个 map
    capti0n
    capti0n
    Jan 25, 2021
    8
    个人理解:
    1.golang 的 map 是 hashmap,会默认分配一部分桶出来,这时并行写入或者访问没问题,
    当超过一定阈值,会触发扩容,这时就会有并发问题。
    2.sync.map 有试过吗?
    yujianwjj
    yujianwjj
    OP
    Jan 25, 2021
    9
    @kiddingU 你说的 concurrent map 是这个吗? https://github.com/orcaman/concurrent-map
    Jooooooooo
    Jooooooooo
    Jan 25, 2021
    10
    第一个疑问再次说明并发 bug 很难发现.
    kiddingU
    kiddingU
    Jan 25, 2021
    11
    @yujianwjj 是的,也是加锁,只不过是对锁进行了 shard,减轻锁的粒度
    YUX
    YUX
    Jan 25, 2021 via iPhone
    12
    @kiddingU 好的 学习了
    cloverstd
    cloverstd
    Jan 25, 2021
    13
    比较好奇😯,什么业务场景下是只写不读的
    如果只写,是不是可以考虑换个 free-lock 的数据结构来存
    nuk
    nuk
    Jan 25, 2021
    15
    因为加的越多 hash 冲突就越多,添加一个键花的时间就越久,超过了启动一个 goroutine 的时间,就会报错了。
    用无锁队列然后单线程写好一点吧,写 map 应该不是瓶颈
    ihipop
    ihipop
    Jan 25, 2021 via Android
    16
    @nuk 弄个 chanel 单向灌进去就行吧。。
    nuk
    nuk
    Jan 25, 2021
    17
    @ihipop channel 有锁的呀,太多 goroutine 写就不行了
    YouLMAO
    YouLMAO
    Jan 25, 2021
    18
    楼主是只写不读, 必须用 mutex, 连 rwmutex 都不要, 必须比 sync.Map 性能好, 我说的, 性能经过 G 家认证
    raaaaaar
    raaaaaar
    Jan 25, 2021 via Android
    19
    为什么只写不读?只写的的话那些数据有什么用,是什么业务场景啊
    felixin
    felixin
    Jan 25, 2021 via Android
    20
    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
    xmge
    Jan 25, 2021
    21
    1. 概率问题,对共享资源同时操作肯定会报错
    2. 如果是只读不写,只能加锁,sync.map 也不要用,sync.map 底层是读写分离,写时加锁。
    yzbythesea
    yzbythesea
    Jan 26, 2021
    22
    chan or mutex lock
    Kinnice
    Kinnice
    Jan 26, 2021
    23
    你电脑性能有点好
    yujianwjj
    yujianwjj
    OP
    Jan 26, 2021
    24
    抱歉,题目描述有误,我的场景是先写后读,先加载大量的数据到 map 里面,后面再查找。
    march1993
    march1993
    Jan 26, 2021
    25
    用 goroutine+chan 啊,一个 routine 专门读 chan 然后修改 map,其他 routine 把要修改的内容发到 chan 里
    sunshinev
    sunshinev
    Jan 26, 2021
    26
    sync.Map
    mengdodo
    mengdodo
    Jan 26, 2021
    27
    我记得当初看到过这样一句话:Go 中的 Map 类型不是一种安全的数据类型。所以我比较菜,直接写到 redis 中
    kiddingU
    kiddingU
    Jan 26, 2021
    28
    说用 chan 的,chan 底层数据结构是个啥,有研究过吗~
    Dongxiem
    Dongxiem
    Jan 27, 2021
    29
    @kiddingU 这个很难说的清楚的吧?可以看看这个 深度解密 Go 语言之 channel ( https://zhuanlan.zhihu.com/p/74613114 )
    kiddingU
    kiddingU
    Jan 27, 2021
    30
    有啥难说清楚的,看源码不就清楚了~
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   4488 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 99ms · UTC 06:10 · PVG 14:10 · LAX 22:10 · JFK 01:10
    ♥ Do have faith in what you're doing.