小编典典

Golang 映射对于并发读/写操作的安全性如何?

go

任何人都可以详细说明这一点吗?跨例程的并发读取操作似乎是允许的,但如果尝试读取和写入同一个键,并发读/写操作可能会产生竞争条件。

在某些情况下,这最后的风险可以降低吗?例如:

  • 函数A生成k并设置m[k]=0。这是 A 写入映射 m 的唯一一次。已知 k 不在 m 中。
  • A 将 k 传递给同时运行的函数 B
  • A 然后读取 m[k]。如果 m[k]==0,则等待,仅当 m[k]!=0 时继续
  • B 在地图中寻找 k。如果找到,B 将 m[k] 设置为某个正整数。如果不是,它会等到 k 在 m 中。

这不是代码(显然),但我认为它显示了一种情况的轮廓,即使 A 和 B 都尝试访问 m 也不会有竞争条件,或者如果存在竞争条件也没关系,因为额外的约束。


阅读 216

收藏
2021-12-16

共1个答案

小编典典

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

更改read功能:

func read() {
    // lock.RLock()
    // defer lock.RUnlock()
    _ = m["a"]
}

在此处输入图片说明

另一种选择:

众所周知,map是由桶实现的,sync.RWMutex会锁定所有的桶。concurrent-map用于fnv32对键进行分片,每个存储桶使用一个sync.RWMutex.

2021-12-16