Golang 映射对于并发读/写操作有多安全?

新手上路,请多包涵

根据 Go 博客,

映射对于并发使用是不安全的:它没有定义当你同时读取和写入它们时会发生什么。如果您需要从并发执行的 goroutine 读取和写入映射,则访问必须通过某种同步机制进行调解。 (来源: 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 在地图中寻找 k。如果找到它,B 将 m[k] 设置为某个正整数。如果没有,它会一直等到 k 在 m 中。

这不是代码(显然),但我认为它显示了一个案例的轮廓,即使 A 和 B 都尝试访问 m 也不会有竞争条件,或者如果有竞争条件也不重要,因为额外的约束。

原文由 John D. 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 590
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 将锁定所有桶。 并发映射 使用 fnv32 对密钥进行分片,每个桶使用一个 sync.RWMutex

原文由 Bryce 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏