Golang Data Race,退出状态为 66

我有以下代码,并且正在进行数据竞争。Round 函数定期检查运行删除地图内容的函数正如我在这里读到的:

从地图中删除数据是安全的,但我有数据竞争


package main


import (

    "fmt"

    "sync"

    "time"

)


type City struct {

    ID string

}


type Map struct {

    sync.RWMutex

    Data map[string]City

}


var done = make(chan struct{})


func (m *Map) Round() {

    for {

        select {

        case <-time.After(2 * time.Second):

            for i, v := range m.Data {

                fmt.Println("-----", v)

                delete(m.Data, i)

            }

        case <-done:

            println("bye")

            break

        }

    }

}


func (m *Map) Add(id string, h City) {

    m.Lock()

    m.Data[id] = h

    m.Unlock()

}


func main() {

    m := Map{}

    m.Data = make(map[string]City)


    m.Data["Ottowa"] = City{"Canada"}

    m.Data["London"] = City{"GB"}

    m.Data["malafya"] = City{"malafya"}


    go m.Round()


    for i := 0; i < 4; i++ {

        go func() {

            time.Sleep(2 * time.Second)

            go m.Add("uz", City{"CityMakon"})

            go m.Add("uzb", City{"CityMakon"})

        }()

    }

    time.Sleep(5 * time.Second)

    done <- struct{}{}

}

输出:


----- {Canada}

----- {GB}

----- {malafya}

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

WARNING: DATA RACE

Write by goroutine 12:

  runtime.mapassign1()

      /usr/lib/golang/src/runtime/hashmap.go:411 +0x0

  main.(*Map).Add()

      /home/narkoz/elixir/round.go:37 +0xaa


Previous write by goroutine 6:

  runtime.mapdelete()

      /usr/lib/golang/src/runtime/hashmap.go:511 +0x0

  main.(*Map).Round()

      /home/narkoz/elixir/round.go:26 +0x3a9


Goroutine 12 (running) created at:

  main.main.func1()

      /home/narkoz/elixir/round.go:54 +0x8c


Goroutine 6 (running) created at:

  main.main()

      /home/narkoz/elixir/round.go:49 +0x2af

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

----- {CityMakon}

----- {CityMakon}

Found 1 data race(s)

exit status 66

但是当我将映射的值类型更改为 int 或 string 时,没有数据竞争。


您推荐什么解决方案?


偶然的你
浏览 229回答 1
1回答

12345678_0001

更新:但是当我将映射的值类型更改为 int 或 string 时,没有数据竞争。我进一步测试了你的代码。将映射的值类型更改为 int 或 string 会继续产生竞争条件。尝试while在你的 shell 上循环运行它,你会明白我的意思:$ while true; do go run -race main.go; done值类型之间不应该有任何差异。正如竞态检测器所报告的,有两种不同的竞态条件。第一场比赛(您已修复)发生在i第 54 行的读取 (of ) 和第i51 行的写入 (to ) 之间。发生这种情况是因为您的 goroutine 闭包包含对 的引用,该引用由goroutine中的循环i更改. 您可以通过摆脱或传递给您的闭包来解决它,如下所示:formainprintln(">>", i)ifor i := 0; i < 4; i++ {&nbsp; go func(index int) {&nbsp; &nbsp; time.Sleep(2 * time.Second)&nbsp; &nbsp; println(">>", index)&nbsp; &nbsp; go m.Add("uz", City{"CityMakon"})&nbsp; &nbsp; go m.Add("uzb", City{"CityMakon"})&nbsp; }(i)}第二个竞争条件发生在第 37 行 ( m.Data[id] = h) 的赋值和第 25 行 ( ) 的删除之间delete(m.Data, i)。竞态检测器将此标记为竞态条件,因为它不能保证您的代码上的Happen Before约束。您可以通过以下任一方式解决此问题:锁定delete语句:m.Lock()delete(m.Data, i)m.Unlock()或者,将Round()方法中的两种情况提取为两种方法,覆盖通道:func (m *Map) Round() {&nbsp; for i, v := range m.Data {&nbsp; &nbsp; fmt.Println("-----", v)&nbsp; &nbsp; delete(m.Data, i)&nbsp; }}func (m *Map) Done() {&nbsp; for range done {&nbsp; &nbsp; println("bye")&nbsp; &nbsp; break&nbsp; }}func main() {&nbsp; // ...&nbsp; go Round()&nbsp; go Done()}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go