根据 Go 博客,
Maps are not safe for concurrent use: it's not defined what happens when you read and write to them simultaneously. If you need to read from and write to a map from concurrently executing goroutines, the accesses must be mediated by some kind of synchronization mechanism. (source: https://blog.golang.org/go-maps-in-action)
谁能详细说明一下?跨例程的并发读取操作似乎是允许的,但如果尝试读取和写入同一个键,则并发读取/写入操作可能会产生竞争条件。
在某些情况下可以降低最后一个风险吗?例如:
- 函数 A 生成 k 并设置 m[k]=0。这是 A 唯一一次写入映射 m。已知 k 不在 m 中。
- A 将 k 传递给同时运行的函数 B
- A 然后读取 m[k]。如果 m[k]==0,它会等待,仅当 m[k]!=0 时才继续
- B 在 map 中寻找 k。如果找到它,B 将 m[k] 设置为某个正整数。如果不是,它会等到 k 在 m 中。
这不是代码(显然),但我认为它显示了即使 A 和 B 都尝试访问 m 也不会出现竞争条件的情况的轮廓,或者如果存在则无关紧要因为额外的限制。
最佳答案
在Golang 1.6之前,并发读是可以的,并发写是不行的,但是写并发读是可以的。从 Golang 1.6 开始,map 在写入时无法读取。 所以在Golang 1.6之后,并发访问图应该是这样的:
package main
import (
"sync"
"time"
)
var m = map[string]int{"a": 1}
var lock = sync.RWMutex{}
func main() {
go Read()
time.Sleep(1 * time.Second)
go Write()
time.Sleep(1 * time.Minute)
}
func Read() {
for {
read()
}
}
func Write() {
for {
write()
}
}
func read() {
lock.RLock()
defer lock.RUnlock()
_ = m["a"]
}
func write() {
lock.Lock()
defer lock.Unlock()
m["b"] = 2
}
添加:
您可以使用 go run -race race.go
更改读取
功能:
func read() {
// lock.RLock()
// defer lock.RUnlock()
_ = m["a"]
}
另一个选择:
众所周知,map
是由桶实现的,sync.RWMutex
会锁定所有桶。 concurrent-map使用 fnv32
对 key 进行分片,每个桶使用一个 sync.RWMutex
。
关于go - 并发读/写操作的 Golang 映射有多安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36167200/