go map 并发写的问题

2021-01-25 14:07:39 +08:00
 yujianwjj

场景是多个 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 只写不读的场景有什么效率更高的方式。
5745 次点击
所在节点    Go 编程语言
30 条回复
YUX
2021-01-25 14:12:58 +08:00
用 sync.Map
Takamine
2021-01-25 14:18:04 +08:00
5000 报错,100 不报错就是单纯概率问题吧。

只写不读可以给 map 包一层方法,在写的地方加锁。
BeautifulSoap
2021-01-25 14:18:28 +08:00
因为你 count 5000 的时候触发 map 同时写入的几率非常高啊。。。

100 一下子就执行完毕了数量也少同时写入几率小
kiddingU
2021-01-25 14:19:36 +08:00
@YUX 看楼主的场景是只写不读,sync.Map 不适合这种场景,锁的粒度太大, 用 concurrent map 就行,或者自己写一个算法,减小锁的粒度
MidGap
2021-01-25 14:20:12 +08:00
哈哈哈哈哈好可爱
JKeita
2021-01-25 14:20:23 +08:00
我这 10 都报错,可能跟电脑 CPU 性能有关吧,把数据先并发入 chan,再按顺序读取写入 map ?
monsterxx03
2021-01-25 14:23:00 +08:00
100 的时候你多试几次就挂了,或者加 -race.
sync.Map 只对读多写少的场景有效率提升.
单 map 每次写加锁可能还不如顺序写.
一般优化思路是做 sharding, 比如预先分配 8 个 map, 每次写的时候 i%8 决定写入哪个 map
capti0n
2021-01-25 14:23:27 +08:00
个人理解:
1.golang 的 map 是 hashmap,会默认分配一部分桶出来,这时并行写入或者访问没问题,
当超过一定阈值,会触发扩容,这时就会有并发问题。
2.sync.map 有试过吗?
yujianwjj
2021-01-25 14:25:57 +08:00
@kiddingU 你说的 concurrent map 是这个吗? https://github.com/orcaman/concurrent-map
Jooooooooo
2021-01-25 14:28:22 +08:00
第一个疑问再次说明并发 bug 很难发现.
kiddingU
2021-01-25 14:39:26 +08:00
@yujianwjj 是的,也是加锁,只不过是对锁进行了 shard,减轻锁的粒度
YUX
2021-01-25 14:42:37 +08:00
@kiddingU 好的 学习了
cloverstd
2021-01-25 16:22:47 +08:00
比较好奇😯,什么业务场景下是只写不读的
如果只写,是不是可以考虑换个 free-lock 的数据结构来存
joesonw
2021-01-25 17:04:49 +08:00
nuk
2021-01-25 17:26:39 +08:00
因为加的越多 hash 冲突就越多,添加一个键花的时间就越久,超过了启动一个 goroutine 的时间,就会报错了。
用无锁队列然后单线程写好一点吧,写 map 应该不是瓶颈
ihipop
2021-01-25 19:47:30 +08:00
@nuk 弄个 chanel 单向灌进去就行吧。。
nuk
2021-01-25 20:45:57 +08:00
@ihipop channel 有锁的呀,太多 goroutine 写就不行了
YouLMAO
2021-01-25 21:35:18 +08:00
楼主是只写不读, 必须用 mutex, 连 rwmutex 都不要, 必须比 sync.Map 性能好, 我说的, 性能经过 G 家认证
raaaaaar
2021-01-25 23:02:00 +08:00
为什么只写不读?只写的的话那些数据有什么用,是什么业务场景啊
felixin
2021-01-25 23:38:06 +08:00
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

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/748150

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX