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

时间:2018-08-18 16:24:14

标签: go concurrency goroutine

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

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

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

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 

4 个答案:

答案 0 :(得分:1)

  

如果并发进程将全局变量写入   值一样吗?

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

运行Go数据竞赛检测器。

参考文献:

Wikipedia: Race condition

Benign Data Races: What Could Possibly Go Wrong?

The Go Blog: Introducing the Go Race Detector

Go: Data Race Detector


  

Go 1.8 Release Notes

     

Concurrent Map Misuse

     

在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

$

答案 1 :(得分:0)

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

答案 2 :(得分:0)

如果使用多个go例程同时更改同一值,则最好使用锁。由于互斥量和锁定会在任何时候更改,以确保在另一个函数更改相同值时无法访问值,就像在访问同一表时写入数据库表一样。

对于使用具有不同键的地图的问题,在Go as中不建议使用

  

地图的典型用法不需要​​从多个位置安全访问   goroutine,在某些情况下,地图可能是一部分   一些已经存在的较大数据结构或计算   已同步。因此,要求所有地图操作都必须获取互斥量   会减慢大多数程序的速度,并增加少数程序的安全性。

     

仅当正在进行更新时,地图访问才是不安全的。只要所有   goroutine仅在读取-查找地图中的元素,包括   使用for range循环遍历它-而不更改地图   通过分配元素或进行删除,对他们来说是安全的   同时访问地图而无需同步。

因此,不建议更新地图。有关Check FAQ的更多信息,说明为什么映射操作未定义为原子。

还要注意,如果您确实想去,应该有一种同步它们的方法。

  

地图并不能安全使用:未定义会发生什么情况   当您同时读写它们时。如果您需要阅读   从并发执行的goroutine中写入地图,   访问必须通过某种同步机制来实现。   保护地图的一种常见方法是使用sync.RWMutex。

答案 3 :(得分:0)

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

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