首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >戈朗数据种族,66人退出状态

戈朗数据种族,66人退出状态
EN

Stack Overflow用户
提问于 2016-06-10 18:45:06
回答 1查看 1.4K关注 0票数 3

我有以下代码,并且正在进行数据竞争。循环函数定期检查运行函数以删除地图的内容,正如我在这里所读的:Is it safe to remove selected keys from Golang map within a range loop?

从地图中删除数据是安全的,但我正在进行数据竞赛。

代码语言:javascript
复制
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{}{}
}

产出:

代码语言:javascript
复制
----- {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时,不存在数据竞争。

你推荐什么解决方案?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-06-10 18:51:17

最新情况:

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

我进一步测试了你的代码。将映射的值类型更改为int或string将继续产生争用条件。尝试在shell上的while循环中运行它,您将看到我的意思:

代码语言:javascript
复制
$ while true; do go run -race main.go; done

值类型之间不应该有任何区别。

正如种族探测器所报告的,有两种不同的竞赛条件。第一次竞赛(您已经修正了)发生在第54行的读( i)和第51行的写(到i)之间。之所以会发生这种情况,是因为您的goroutine闭包包含对i的引用,for循环在main goroutine中对该引用进行了更改。您可以通过去掉println(">>", i)或将i传递到闭包中来解决这个问题,如下所示:

代码语言:javascript
复制
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))之间。竞争检测器将此标记为竞争条件,因为它不能保证代码上的https://golang.org/ref/mem#tmp_2约束。您可以通过以下两种方法来解决这一问题:

锁定delete语句:

代码语言:javascript
复制
m.Lock()
delete(m.Data, i)
m.Unlock()

或者,将Round()方法中的两种情况提取为两种方法,范围在通道上:

代码语言:javascript
复制
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()
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37755250

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档