从文件解析数据时大小不同的地图
Map of maps varying in size when parsing data from a file
我正在为受 this project 启发的 Nagios 构建 API。我已经开始重新创建读取 status.dat 文件并将数据存储在许多对象中的代码,这些对象随后用于创建主机、服务、信息字典,这些信息字典位于 core.py 文件中。
下面是我的 python 代码的 Go 版本,它似乎按预期工作。它仍处于早期阶段,所以对于任何编码错误的做法,我深表歉意。
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
}
在 main 函数中,我创建了我的嵌套映射以暂时只保存主机数据,并且没有任何抱怨地完成了。我遇到的问题是,当我检查这张地图的长度时,我希望看到 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.go
93
第二个运行:
$ go run -race mytest.go
95
第三个运行:
$ go run -race mytest.go
63
你懂的。
我觉得问题出在地图上,因为如果我只是打印主机而不将它们放入地图,我会看到所有我期望的主机。地图在每个 运行 上大小不同的原因是什么?
您的代码具有以下竞争条件
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
}
我可以通过在 current
地图发送到频道后清空它来解决我的问题。
myChannel <- current
current = make(map[string]string)
然后在 for obj := range hoststatus
循环之后的 main()
函数中,我将这些数据放入一个单独的映射中,然后从中开始工作。
hostStatusMap := make(map[string]string)
for k, v := range obj {
hostStatusMap[k] = v
}
我还能够删除散布在整个代码中的锁,现在 returns 每个 运行 上主机的正确长度。
我正在为受 this project 启发的 Nagios 构建 API。我已经开始重新创建读取 status.dat 文件并将数据存储在许多对象中的代码,这些对象随后用于创建主机、服务、信息字典,这些信息字典位于 core.py 文件中。
下面是我的 python 代码的 Go 版本,它似乎按预期工作。它仍处于早期阶段,所以对于任何编码错误的做法,我深表歉意。
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
}
在 main 函数中,我创建了我的嵌套映射以暂时只保存主机数据,并且没有任何抱怨地完成了。我遇到的问题是,当我检查这张地图的长度时,我希望看到 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.go
93
第二个运行:
$ go run -race mytest.go
95
第三个运行:
$ go run -race mytest.go
63
你懂的。
我觉得问题出在地图上,因为如果我只是打印主机而不将它们放入地图,我会看到所有我期望的主机。地图在每个 运行 上大小不同的原因是什么?
您的代码具有以下竞争条件
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
}
我可以通过在 current
地图发送到频道后清空它来解决我的问题。
myChannel <- current
current = make(map[string]string)
然后在 for obj := range hoststatus
循环之后的 main()
函数中,我将这些数据放入一个单独的映射中,然后从中开始工作。
hostStatusMap := make(map[string]string)
for k, v := range obj {
hostStatusMap[k] = v
}
我还能够删除散布在整个代码中的锁,现在 returns 每个 运行 上主机的正确长度。