为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
料青山看我应如是 2021-12-27 15:39:40
根据 Go 博客,映射对于并发使用是不安全的:它没有定义当您同时读取和写入它们时会发生什么。如果您需要从并发执行的 goroutine 中读取和写入映射,则访问必须通过某种同步机制进行调解。(来源:https : //blog.golang.org/go-maps-in-action)任何人都可以详细说明这一点吗?跨例程的并发读取操作似乎是允许的,但如果尝试读取和写入同一个键,并发读/写操作可能会产生竞争条件。在某些情况下,这最后的风险可以降低吗?例如:函数A生成k并设置m[k]=0。这是 A 写入映射 m 的唯一一次。已知 k 不在 m 中。A 将 k 传递给同时运行的函数 BA 然后读取 m[k]。如果 m[k]==0,则等待,仅当 m[k]!=0 时继续B 在地图中寻找 k。如果找到,B 将 m[k] 设置为某个正整数。如果不是,它会等到 k 在 m 中。这不是代码(显然),但我认为它显示了一种情况的轮廓,即使 A 和 B 都尝试访问 m 也不会有竞争条件,或者如果存在竞争条件也没关系,因为额外的约束。
查看完整描述

2 回答

?
开满天机

TA贡献1786条经验 获得超13个赞

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

}

或者你会得到以下错误:

//img1.sycdn.imooc.com//61c96dd90001bc9906470367.jpg

添加:


您可以通过使用检测种族 go run -race race.go


更改read功能:


func read() {

    // lock.RLock()

    // defer lock.RUnlock()

    _ = m["a"]

}

//img1.sycdn.imooc.com//61c96de400011def08720531.jpg

另一种选择:

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


查看完整回答
反对 回复 2021-12-27
?
森栏

TA贡献1810条经验 获得超5个赞

Go 1.6 发行说明

运行时添加了轻量级、尽力而为的并发误用映射检测。与往常一样,如果一个 goroutine 正在写入地图,则没有其他 goroutine 应该同时读取或写入地图。如果运行时检测到这种情况,它会打印诊断信息并使程序崩溃。找出更多问题的最好方法是在比赛检测器下运行程序,这将更可靠地识别比赛并提供更多细节。

映射是复杂的、自重组的数据结构。并发读写访问未定义。

没有代码,没有什么可说的。


查看完整回答
反对 回复 2021-12-27
  • 2 回答
  • 0 关注
  • 156 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信