英文:
Incorrect output by goroutines
问题
我正在观看一个讲座,作者在其中使用Go协程构建了一个状态应用程序,但在一段时间后表现出奇怪的行为。
以下是代码:
func main() {
links := []string{
"http://google.com",
"http://facebook.com",
"http://stackoverflow.com",
"http://amazon.com",
"http://golang.org",
}
c := make(chan string)
for _, link := range links {
go checkLink(link, c)
}
for l := range c {
go func() {
time.Sleep(5 * time.Second)
checkLink(l, c)
}()
}
}
func checkLink(link string, c chan string) {
_, err := http.Get(link)
if err != nil {
fmt.Println(link, "可能已经宕机!")
c <- link
return
}
fmt.Println(link, "正常运行!")
c <- link
}
我得到的输出是:
http://stackoverflow.com 正常运行!
http://google.com 正常运行!
http://facebook.com 正常运行!
http://golang.org 正常运行!
http://amazon.com 正常运行!
http://amazon.com 正常运行!
http://amazon.com 正常运行!
http://amazon.com 正常运行!
http://amazon.com 正常运行!
http://amazon.com 正常运行!
我不明白为什么"amazon.com"一直重复出现在这里?为什么在打印了所有5个链接之后会发生这种情况?
英文:
I am watching a lecture in which author build a status app using go routines which is behaving weirdly after sometime.
Here's the code:
func main() {
links := []string{
"http://google.com",
"http://facebook.com",
"http://stackoverflow.com",
"http://amazon.com",
"http://golang.org",
}
c := make(chan string)
for _, link := range links {
go checkLink(link, c)
}
for l := range c {
go func() {
time.Sleep(5 * time.Second)
checkLink(l, c)
}()
}
}
func checkLink(link string, c chan string) {
_, err := http.Get(link)
if err != nil {
fmt.Println(link, "might be down!")
c <- link
return
}
fmt.Println(link, "is up!")
c <- link
}
The output I get is:
> http://stackoverflow.com is up!
>
> http://google.com is up!
>
> http://facebook.com is up!
>
> http://golang.org is up!
>
> http://amazon.com is up!
>
> http://amazon.com is up!
>
> http://amazon.com is up!
>
> http://amazon.com is up!
>
> http://amazon.com is up!
>
> http://amazon.com is up!
I don't understand why the "amazon.com" keeps on repeating here?? Why is this happening after all the 5 links has been printed?
答案1
得分: 1
问题出在闭包捕获循环变量l
上。由于goroutine是在循环内部启动的,循环创建的所有goroutine共享相同的内存地址l
。当休眠结束并执行goroutine时,由于循环迭代剩余的链接,l
的值已经发生了变化。因此,所有的goroutine最终都会检查links
切片中的最后一个链接,即**"http://amazon.com"**。
要解决这个问题,你需要将循环变量l
作为参数传递给goroutine内部的匿名函数。下面是代码的更新版本:
for l := range c {
go func(link string) {
time.Sleep(5 * time.Second)
checkLink(link, c)
}(l)
}
通过将l
作为参数传递给匿名函数,每个goroutine都会拥有自己的l
的副本,保留了每次迭代的正确值。
英文:
The issue lies in the closure capturing of the loop variable l
. Since the goroutine is started inside a loop, all goroutines created by the loop share the same memory address for l
. By the time the sleep is over and the goroutine is executed, the value of l
has changed due to the loop iterating over the remaining links. Consequently, all goroutines end up checking the last link in the links
slice, which is "http://amazon.com".
To fix this, you need to pass the loop variable l
as an argument to the anonymous function inside the goroutine. Here's an updated version of the code:
for l := range c {
go func(link string) {
time.Sleep(5 * time.Second)
checkLink(link, c)
}(l)
}
By passing l
as an argument to the anonymous function, each goroutine will have its own copy of l
, preserving the correct value for each iteration.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论