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

如果并发进程向全局变量写入相同的值会怎样?

如果并发进程向全局变量写入相同的值会怎样?

Go
慕码人2483693 2023-03-21 15:45:42
我只是想知道是否有可能由于同时将相同的值写入全局变量而导致损坏。我的大脑告诉我这没有任何问题,因为它只是内存中的一个位置,但我认为我应该仔细检查这个假设。我有写入全局地图的并发进程var linksToVisit map[string]bool。该地图实际上是在跟踪网站上的哪些链接需要进一步抓取。然而,并发进程可能在它们各自的页面上有相同的链接,因此每个进程都会将相同的链接标记为true并发。在这种情况下不使用锁没有错吧?注意:我从不将值改回,false所以要么键存在并且它的值为真,要么它不存在。IEvar linksToVisit = map[string]bool{}... // somewhere later a goroutine finds a link and marks it as true// it is never marked as false anywherelinksToVisit[someLink] = true 
查看完整描述

4 回答

?
白衣非少年

TA贡献1155条经验 获得超0个赞

如果并发进程向全局变量写入相同的值会怎样?

数据竞争的结果是不确定的。

运行 Go 数据竞争检测器。

参考:

维基百科:比赛条件

良性数据竞赛:可能出现什么问题?

Go 博客:介绍 Go 竞速检测器

Go:数据竞争检测器


Go 1.8 发行说明

并发映射滥用

在 Go 1.6 中,运行时添加了轻量级的、最大努力的映射并发滥用检测。此版本改进了该检测器,支持检测同时写入和遍历映射的程序。

与往常一样,如果一个 goroutine 正在写入 map,则没有其他 goroutine 应该同时读取(包括迭代)或写入 map。如果运行时检测到这种情况,它会打印诊断并使程序崩溃。找出更多有关问题的最佳方法是在竞争检测器下运行程序,这将更可靠地识别竞争并提供更多详细信息。


例如,

package main


import "time"


var linksToVisit = map[string]bool{}


func main() {

    someLink := "someLink"

    go func() {

        for {

            linksToVisit[someLink] = true

        }

    }()

    go func() {

        for {

            linksToVisit[someLink] = true

        }

    }()

    time.Sleep(100 * time.Millisecond)

}

输出:


$ go run racer.go

fatal error: concurrent map writes

$


$ go run -race racer.go


==================

WARNING: DATA RACE

Write at 0x00c000078060 by goroutine 6:

  runtime.mapassign_faststr()

      /home/peter/go/src/runtime/map_faststr.go:190 +0x0

  main.main.func2()

      /home/peter/gopath/src/racer.go:16 +0x6a


Previous write at 0x00c000078060 by goroutine 5:

  runtime.mapassign_faststr()

      /home/peter/go/src/runtime/map_faststr.go:190 +0x0

  main.main.func1()

      /home/peter/gopath/src/racer.go:11 +0x6a


Goroutine 6 (running) created at:

  main.main()

      /home/peter/gopath/src/racer.go:14 +0x88


Goroutine 5 (running) created at:

  main.main()

      /home/peter/gopath/src/racer.go:9 +0x5b

==================


fatal error: concurrent map writes


$


查看完整回答
反对 回复 2023-03-21
?
白猪掌柜的

TA贡献1893条经验 获得超10个赞

如果您使用多个 go 例程同时更改相同的值,则最好使用锁。由于当另一个函数正在更改相同值时,无论何时都使用互斥锁和锁来保护值不被访问,就像在访问同一个表时写入数据库表一样。

对于您关于使用具有不同键的地图的问题,在 Go 中并不可取,因为:

map 的典型使用不需要从多个 goroutine 进行安全访问,在需要的情况下,map 可能是某个更大的数据结构或已经同步的计算的一部分。因此,要求所有映射操作都获取一个互斥量会减慢大多数程序的速度并增加少数程序的安全性。

只有在发生更新时,地图访问才是不安全的。只要所有 goroutines 都只是读取——查找地图中的元素,包括使用 for range 循环遍历它——而不是通过分配给元素或进行删除来更改地图,它们就可以安全地同时访问地图而无需同步。

因此,不建议更新地图。有关详细信息,请查看关于为什么映射操作未定义为原子的常见问题解答。

还注意到,如果你真的想去 for 应该有一种方法来同步它们。

映射对于并发使用是不安全的:它没有定义当你同时读取和写入它们时会发生什么。如果您需要从并发执行的 goroutine 读取和写入映射,则访问必须通过某种同步机制进行调解。保护地图的一种常见方法是使用 sync.RWMutex。


查看完整回答
反对 回复 2023-03-21
?
ibeautiful

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

并发映射写入不正常,因此您很可能会遇到致命错误。所以我认为应该使用锁



查看完整回答
反对 回复 2023-03-21
?
侃侃尔雅

TA贡献1801条经验 获得超15个赞

从 Go 1.6 开始,同时写入 map 将导致panic. 使用 async.Map同步访问。

查看映射值分配实现: https://github.com/golang/go/blob/fe8a0d12b14108cbe2408b417afcaab722b0727c/src/runtime/hashmap.go#L519


查看完整回答
反对 回复 2023-03-21
  • 4 回答
  • 0 关注
  • 98 浏览
慕课专栏
更多

添加回答

举报

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