-
万千封印
var mu = &sync.RWMutex{}func openStatusFile() *os.File { file, err := os.Open("/usr/local/nagios/var/status.dat") if err != nil { } return file}func nextStanza() <-chan map[string]string { myChannel := make(chan map[string]string) scanner := bufio.NewScanner(openStatusFile()) current := make(map[string]string) go func() { for scanner.Scan() { mainline := scanner.Text() line := strings.TrimSpace(mainline) if strings.HasSuffix(line, "{") { if len(current) != 0 { myChannel <- current } result := strings.SplitN(line, " ", 2) mu.Lock() current["type"] = result[0] mu.Unlock() } else if strings.Contains(line, "=") { result := strings.SplitN(line, "=", 2) key := result[0] val := result[1] mu.Lock() current[key] = val mu.Unlock() } } close(myChannel) }() return myChannel}在主函数中,我创建了嵌套映射以暂时仅保存主机数据,并且完成后没有任何抱怨。我遇到的问题是,当我检查此映射的长度时,我期望看到 104 个主机,但每次运行此测试文件时都会得到不同的结果。func main() { hoststatus := nextStanza() hosts := make(map[string]map[string]string) // services := make(map[string]map[string]map[string]string) var host string // var service string for obj := range hoststatus { var hostPlaceHolder string var typePlaceHolder string mu.Lock() hostPlaceHolder = obj["host_name"] mu.Unlock() if hostPlaceHolder != "" { host = hostPlaceHolder } mu.Lock() typePlaceHolder = obj["type"] mu.Unlock() if typePlaceHolder == "hoststatus" { mu.Lock() hosts[host] = obj mu.Unlock() } } fmt.Println(len(hosts))}第一次运行:$ go run -race mytest.go93第二次运行:$ go run -race mytest.go95第三次运行:$ go run -race mytest.go63你明白了。我觉得问题与地图有关,因为如果我只是打印主机而不将它们放入地图中,我会看到我期望的所有主机。每次运行时地图尺寸不同的原因是什么?
-
慕无忌1623718
我能够通过在将地图current发送到频道后清空地图来解决我的问题。myChannel <- currentcurrent = make(map[string]string)然后在循环main()后的函数中for obj := range hoststatus,我将这些数据放入一个单独的映射中,然后进行处理。hostStatusMap := make(map[string]string) for k, v := range obj { hostStatusMap[k] = v }我还能够删除分散在代码中的锁,现在它在每次运行时返回正确的主机长度。
-
holdtom
您的代码具有以下竞争条件func nextStanza() <-chan map[string]string { myChannel := make(chan map[string]string) scanner := bufio.NewScanner(openStatusFile()) current := make(map[string]string) go func() { for scanner.Scan() { mainline := scanner.Text() line := strings.TrimSpace(mainline) if strings.HasSuffix(line, "{") { if len(current) != 0 { myChannel <- current } result := strings.SplitN(line, " ", 2) mu.Lock() current["type"] = result[0] mu.Unlock() } else if strings.Contains(line, "=") { result := strings.SplitN(line, "=", 2) key := result[0] val := result[1] mu.Lock() current[key] = val mu.Unlock() } } close(myChannel) }() return myChannel}当您在匿名函数上启动 goroutine 时,您并没有为其创建 WaitGroup。这意味着该函数nextStanza()将启动 goroutine,而return无需等待匿名 goroutine 终止——从而在父函数关闭时结束 goroutine。我建议使用等待组,这样可以保证匿名函数终止。一个简单的例子说明了正在发生的事情:具有竞争条件import ( "fmt" "time" // "sync" )func main() { go func() { time.Sleep(3 * time.Second) fmt.Println("hai") }() return}没有竞争条件import ( "fmt" "time" "sync" )func main() { var wg sync.WaitGroup wg.Add(1) go func() { time.Sleep(3 * time.Second) fmt.Println("hai") wg.Done() }() wg.Wait() return}