我正在学习Go现在正在使用,我的第一个项目之一就是一个简单的ping脚本.基本上我想ping一堆网址,并在每个网站的响应中等待XXX秒,然后再次ping.这是删节代码:
func main() {
// read our text file of urls
f, err := ioutil.ReadFile(urlFile)
if err != nil {
log.Print(err)
}
urlStrings := []string{}
urlStrings = strings.Split(string(f), "\n")
for _, v := range urlStrings {
go ping(v)
}
// output logs to the terminal
// channel is global
for i := range c {
fmt.Println(i)
}
}
func ping(url string) {
// for our lag timer
start := time.Now()
// make our request
_, err := http.Get(url)
if err != nil {
msg := url + " Error:" + err.Error()
fmt.Println(msg)
c <- msg
reportError(msg)
} else {
lag := time.Since(start)
var msg string
// running slow
if lag > lagThreshold*time.Second {
msg = url + " lag: " + lag.String()
reportError(msg)
}
msg = url + ", lag: " + lag.String()
c <- msg
}
time.Sleep(pingInterval * time.Second)
go ping(url) // is this acceptable?
}
Run Code Online (Sandbox Code Playgroud)
在我的Get请求中,我之前调用了defer res.Body.Close(),但是在应用程序运行一段时间之后就出现了问题.我假设defer不能在响应上调用Close(),直到goroutine被垃圾收集并且res不再存在.
这让我想到如果通过在goroutine中调用goroutine是最佳实践,或者如果我导致函数永远不会退出,那么只有在goroutine被垃圾收集时才会调用defer.
从goroutine中掏出一个新的goroutine本身就很好.
但我怀疑这是解决您问题的最简单,最干净的解决方案.我猜你的第一个版本做了显而易见的事情并在无限循环中ping每个URL.这咬人推迟:一旦函数返回,就会执行延迟调用.(这与goroutine beeing"垃圾收集没有任何关系;实际上goroutines刚刚结束,没有收集).在无休止的循环中你永远不会回来,你只是累积从未执行的deferred调用.你因此永远不会关闭所有开放res.Body和你的内存/无论如何,看到恐慌.
做defer res.Body.Close是一个很好的习语,但不是在无限循环中.
我会尝试你的第一个版本,并在nil错误路径上直接执行res.Body.Close.