我有以下代码,并且我有数据竞赛 Round 函数定期检查运行该函数以删除地图的内容正如我在这里所读:Is it safe to remove selected keys from Golang map within a range loop?
从地图中删除数据是安全的,但我有数据竞赛
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 时,没有数据竞争。
你推荐什么解决方案?
更新:
但是当我将地图的值类型更改为 int 或 string 时,没有数据竞争。
我进一步测试了你的代码。将 map 的值类型更改为 int 或 string 继续产生竞争条件。尝试在你的 shell 上的while
循环中运行它,你会明白我的意思:
$ while true; do go run -race main.go; done
值类型之间不应有任何差异。
正如竞争检测器所报告的那样,有两种不同的竞争条件。第一个竞争(您已修复)发生在第 54 行的 read(ofi
)和第 51 行的 write(toi
)之间。发生这种情况是因为您的 goroutine 闭包包含对i
的引用,该引用由for
通过
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()
}
本站系公益性非盈利分享网址,本文来自用户投稿,不代表码文网立场,如若转载,请注明出处
评论列表(40条)