首页 > 解决方案 > 如果并发进程向全局变量写入相同的值会发生什么?

问题描述

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

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

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

IE

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 

标签: goconcurrencygoroutine

解决方案


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

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

运行 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

$


推荐阅读