golang map 并发读写竞争问题

2021-11-01 16:51:11 +08:00
 zemul
type IdService struct {
 area  int64
 node  int64
 apply map[string]*apply
}

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

2731 次点击
所在节点    Go 编程语言
11 条回复
sdrzlyz
2021-11-01 17:04:02 +08:00
不会引发 panic ,但是这个 value 对应的数据,其它 goroutine 读到的不一定是最新的。

map 本身又没有发生增减的情况,这种场景下,你用 map 图了个啥?
JKeita
2021-11-01 17:58:51 +08:00
不会,不关心数据修改顺序性就没啥影响
sunny352787
2021-11-01 18:01:22 +08:00
牵扯到多线程就用 sync.Map ,不要自己处理,手动加锁的话性能也会慢很多
bruce0
2021-11-01 19:01:51 +08:00
@sunny352787 sync.Map 这个适用于读多写少的情况,如果是写多读少的话 自己加锁 可能会更高。当然,绝大多数并发 情况下, 无脑 sync.map 就好了
rrfeng
2021-11-01 19:37:51 +08:00
你这个 map 并没有写操作,所以没问题。
xmge
2021-11-01 20:00:29 +08:00
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
2021-11-01 20:01:27 +08:00
看错题了!尴尬
stach
2021-11-01 20:45:49 +08:00
只修改指针对象内的元素,不会有数据竞争问题 (并发读写竞争), 会有数据错误问题 (并发安全问题).
前者着重于程序是否可以正常 run, 后者着重于数据是否可以准确的在多线程中 share.

只要是在 多线程, 读写场景, 就要考虑加锁, 或者采用原子操作等方式, 来进行 线程间 同步.
bazingaterry
2021-11-01 21:18:55 +08:00
「修改指针对象内的元素」这里的 map 不存在 data race ,但其他地方不好说。例如指针并发读写请用 https://pkg.go.dev/sync/atomic#Value ,其他拿不准的情况把 https://golang.org/doc/articles/race_detector 打开看看。
DCjanus
2021-11-02 08:42:18 +08:00
只修改指针对象内的元素相当于多线程持有一个指针,并一起修改对应对象,每次修改不是原子的话,可能读到的是中间状态,即使修改是原子的,也有可能读到非预期的值。
zemul
2021-11-02 21:00:24 +08:00
了解了,感谢解答!

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

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

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

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

© 2021 V2EX