如果并发进程向全局变量写入相同的值,会发生什么情况?



我只是想知道同时将相同的值写入全局变量是否有损坏的可能性。我的大脑告诉我这没有错,因为它只是内存中的一个位置,但我想我可能应该仔细检查这个假设。

我有并发进程写入全局地图var linksToVisit map[string]bool。该地图实际上是在跟踪网站上的哪些链接需要进一步抓取。

但是,并发进程可能在其各自的页面上具有相同的链接,因此每个进程都将同时true标记相同的链接。在这种情况下不使用锁没有错吧?注意:我从不将值更改回false因此,要么键存在并且其值为真,要么不存在。

var linksToVisit = map[string]bool{}
... 
// somewhere later a goroutine finds a link and marks it as true
// it is never marked as false anywhere
linksToVisit[someLink] = true 

如果并发进程写入全局变量会发生什么情况 相同的价值?

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

运行 Go 数据竞速检测器。

引用:

维基百科:竞争条件

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

围棋博客:围棋比赛检测器简介

Go:数据竞速检测器


Go 1.8 发行说明

并发映射滥用

在 Go 1.6 中,运行时添加了轻量级、尽力而为的检测 同时滥用地图。此版本改进了该检测器 支持检测并发写入和迭代的程序 在地图上。

与往常一样,如果一个 goroutine 正在写入地图,则没有其他 goroutine。 应该是读取(包括迭代(或写入地图 同时。如果运行时检测到这种情况,它将打印 诊断并导致程序崩溃。了解更多信息的最佳方式 问题是在种族检测器下运行程序,这将 更可靠地识别种族并提供更多细节。


例如

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
$

如果您使用多个 go 例程同时更改相同的值,则最好使用锁。因为每当涉及到保护值不被访问时,就会使用互斥锁和锁,当另一个函数正在更改相同值时,就像在访问同一个表时写入数据库表一样。

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

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

仅当发生更新时,地图访问才不安全。只要所有 goroutines 只是读取 - 在地图中查找元素,包括 使用 for 范围循环遍历它,而不更改地图 通过分配给元素或执行删除,他们可以安全地 在不同步的情况下并发访问地图。

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

还值得注意的是,如果您真的想要去,应该有一种方法可以同步它们。

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

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

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

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

相关内容

最新更新