尚不存在的对象的竞争条件

时间:2019-04-02 07:29:19

标签: go race-condition

我的比赛状况很奇怪。问题在于它发生在尚不存在的对象内部。

这是演示代码:

package main

import (
    //"fmt"
    "time"
)

type Object1 struct {
    A int
    B string
    C []int
    D *Object2
}

type Object2 struct {
    A int
}

func NewObject1() *Object1 {
    return &Object1{
        A: 1,
        B: "abc",
        C: []int{0, 1},
        D: &Object2{},
    }
}

func main() {
    list := []*Object1{}

    tempA := 0
    tempB := ""
    tempC := []int{}
    tempD := &Object2{}

    go func() {
        for {
            for _, object := range list {
                tempA = object.A
                tempB = object.B
                tempC = object.C
                tempD = object.D
            }
        }
    }()

    for {
        list = append(list, NewObject1())

        //fmt.Println("list", list)
        time.Sleep(1 * time.Second)
    }
}

如果我使用-race标志运行它-我收到警告:

WARNING: DATA RACE
Read at 0x00c000094040 by goroutine 5:
  main.main.func1()
      /tmp/race.go:39 +0x84

Previous write at 0x00c000094040 by main goroutine:
  main.main()
      /tmp/race.go:21 +0x2a9

Goroutine 5 (running) created at:
  main.main()
      /tmp/race.go:36 +0x276
==================
==================
WARNING: DATA RACE
Read at 0x00c000094048 by goroutine 5:
  main.main.func1()
      /tmp/race.go:40 +0xbe

Previous write at 0x00c000094048 by main goroutine:
  main.main()
      /tmp/race.go:22 +0x2ca

Goroutine 5 (running) created at:
  main.main()
      /tmp/race.go:36 +0x276
==================
==================
WARNING: DATA RACE
Read at 0x00c000094058 by goroutine 5:
  main.main.func1()
      /tmp/race.go:41 +0x118

Previous write at 0x00c000094058 by main goroutine:
  main.main()
      /tmp/race.go:23 +0x341

Goroutine 5 (running) created at:
  main.main()
      /tmp/race.go:36 +0x276
==================
==================
WARNING: DATA RACE
Read at 0x00c000094070 by goroutine 5:
  main.main.func1()
      /tmp/race.go:42 +0x180

Previous write at 0x00c000094070 by main goroutine:
  main.main()
      /tmp/race.go:24 +0x3b8

Goroutine 5 (running) created at:
  main.main()
      /tmp/race.go:36 +0x276
==================

那怎么可能呢?读取在goroutine内部进行,而写入在NewObject1()内部进行。每个Object1字段有4个错误。 NewObject1()尚未创建对象,无法将其追加到list切片中。因此,list在阅读过程中应该为空或充满正常的已完成对象。

我逐步想到的工作流程:

  1. 列表为空;
  2. 您开始创建新的object1;
  3. 列表仍然为空;
  4. 您创建了一个新对象,然后将其添加到列表中;
  5. 仅现在list具有1个元素;
  6. 发生阅读。

我在这里看不到比赛状况。如果您有不同的看法-请显示您自己的工作流程。

1 个答案:

答案 0 :(得分:5)

种族检测器检测到您同时在内存中读写相同的地址。

根据定义,这是一场数据竞赛。

何时将数据实际放置到该地址(以及是否完全放置在该地址)都没有关系。唯一重要的是,您可以在不同步的情况下在不同的goroutine中访问相同的内存,而这些操作之一就是“写”操作。

两者都不是Go,而是优质资源:

  1. https://preshing.com/20120710/memory-barriers-are-like-source-control-operations/-该博客中的这篇文章以及基本上所有其他文章都是瑰宝。
  2. http://deadlockempire.github.io/-类似拼图的游戏,揭示了同步和并发问题的细微差别