为了账号安全,请及时绑定邮箱和手机立即绑定

Golang Data Race,退出状态为 66

Golang Data Race,退出状态为 66

Go
偶然的你 2022-01-17 17:47:38
我有以下代码,并且正在进行数据竞争。Round 函数定期检查运行删除地图内容的函数正如我在这里读到的:从地图中删除数据是安全的,但我有数据竞争package mainimport (    "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 RACEWrite by goroutine 12:  runtime.mapassign1()      /usr/lib/golang/src/runtime/hashmap.go:411 +0x0  main.(*Map).Add()      /home/narkoz/elixir/round.go:37 +0xaaPrevious write by goroutine 6:  runtime.mapdelete()      /usr/lib/golang/src/runtime/hashmap.go:511 +0x0  main.(*Map).Round()      /home/narkoz/elixir/round.go:26 +0x3a9Goroutine 12 (running) created at:  main.main.func1()      /home/narkoz/elixir/round.go:54 +0x8cGoroutine 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 时,没有数据竞争。您推荐什么解决方案?
查看完整描述

1 回答

?
12345678_0001

TA贡献1802条经验 获得超5个赞

更新:


但是当我将映射的值类型更改为 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)i


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

  go func(index int) {

    time.Sleep(2 * time.Second)

    println(">>", index)

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

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

  }(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() {

  for i, v := range m.Data {

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

    delete(m.Data, i)

  }

}


func (m *Map) Done() {

  for range done {

    println("bye")

    break

  }

}


func main() {

  // ...

  go Round()

  go Done()

}


查看完整回答
反对 回复 2022-01-17
  • 1 回答
  • 0 关注
  • 228 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信