英文:
Go: Anonymous Func Only Taking Last Value in For Range
问题
我想这是一个相当简单的修复。只需循环遍历字符串列表urls
,并在每次循环迭代时打印出每个url
。但是当我添加并发性(使用go
和WaitGroup
)时,它只打印出列表中的最后一个url
。
为什么它只打印出最后一个url
(从匿名函数打印)而不是在循环迭代时逐个打印每个url
?
Go Playground: http://play.golang.org/p/z4IZLY7Mt_
代码:
package main
import (
"fmt"
"sync"
)
var urls = []string{
"http://google.com",
"http://facebook.com",
"http://youtube.com",
"http://yahoo.com",
"http://baidu.com",
"http://wikipedia.org",
"http://twitter.com",
"http://live.com",
"http://amazon.com",
"http://linkedin.com",
"http://google.co.in",
}
func main() {
var wg sync.WaitGroup
for _, url := range urls {
fmt.Println("正确: " + url)
wg.Add(1)
go func() {
fmt.Println("错误: " + url)
wg.Done()
}()
}
wg.Wait()
}
英文:
I imagine this is a pretty easy fix. Just trying loop through the string list urls
and print out each url
as the loop iterates. But when I add in concurrency (with go
and WaitGroup
) all it prints out is the last url
in the list.
Why is it only printing out the last url
(when printed from the anon function) rather than each url
individually as the loop iterates?
Go Playground: http://play.golang.org/p/z4IZLY7Mt_
Code:
package main
import (
"fmt"
"sync"
)
var urls = []string{
"http://google.com",
"http://facebook.com",
"http://youtube.com",
"http://yahoo.com",
"http://baidu.com",
"http://wikipedia.org",
"http://twitter.com",
"http://live.com",
"http://amazon.com",
"http://linkedin.com",
"http://google.co.in",
}
func main() {
var wg sync.WaitGroup
for _, url := range urls {
fmt.Println("correct: " + url)
wg.Add(1)
go func() {
fmt.Println("wrong: " + url)
wg.Done()
}()
}
wg.Wait()
}
答案1
得分: 0
以下是工作代码:
package main
import (
"fmt"
"sync"
)
var urls = []string{
"http://google.com",
"http://facebook.com",
"http://youtube.com",
"http://yahoo.com",
"http://baidu.com",
"http://wikipedia.org",
"http://twitter.com",
"http://live.com",
"http://amazon.com",
"http://linkedin.com",
"http://google.co.in",
}
func main() {
var wg sync.WaitGroup
for _, url := range urls {
fmt.Println("正确的:", url)
wg.Add(1)
go func(url string) {
fmt.Println("错误的:", url)
wg.Done()
}(url)
}
wg.Wait()
}
还可以在Go Playground上查看:http://play.golang.org/p/zFSDvfdIDS
英文:
Here is the working code:
package main
import (
"fmt"
"sync"
)
var urls = []string{
"http://google.com",
"http://facebook.com",
"http://youtube.com",
"http://yahoo.com",
"http://baidu.com",
"http://wikipedia.org",
"http://twitter.com",
"http://live.com",
"http://amazon.com",
"http://linkedin.com",
"http://google.co.in",
}
func main() {
var wg sync.WaitGroup
for _, url := range urls {
fmt.Println("correct: " + url)
wg.Add(1)
go func(url string) {
fmt.Println("wrong: " + url)
wg.Done()
}(url)
}
wg.Wait()
}
Also on go playground: http://play.golang.org/p/zFSDvfdIDS
答案2
得分: 0
因为接受的答案没有解释为什么,所以我添加了我的答案。
在Go语言中,闭包(closure)可以访问其创建时所在作用域中的所有变量。因此,在下面的示例中创建的每个闭包都可以访问循环变量url
。
因此,当每个闭包开始并发运行时,url
的值可以是urls
切片的任何元素(大多数情况下是最后一个元素,但并不总是),因为它不会阻塞main
Go协程。为了让每个闭包使用正确的值,我们需要复制正确的值来使用。例如,你可以这样做:
func main() {
var wg sync.WaitGroup
for _, url := range urls {
fmt.Println("correct: " + url)
url2 := url
wg.Add(1)
go func() {
fmt.Println("wrong: " + url2)
wg.Done()
}()
}
wg.Wait()
}
或者可以使用上一个答案中已经提到的方法,这种方法更明确和可读:
func main() {
var wg sync.WaitGroup
for _, url := range urls {
fmt.Println("correct: " + url)
wg.Add(1)
go func(url string) {
fmt.Println("wrong: " + url)
wg.Done()
}(url)
}
wg.Wait()
}
英文:
Adding my answer because the accepted one does not explain WHY.
A closure in Go has access to all the variables of the scope where it's created. So every closure being created in the example below has access to the loop variable url
.
func main() {
var wg sync.WaitGroup
for _, url := range urls {
fmt.Println("correct: " + url)
wg.Add(1)
go func() {
fmt.Println("wrong: " + url)
wg.Done()
}()
}
wg.Wait()
}
Therefore, when every closure starts running concunrrently, value of url
can be any element of urls
slice(in most cases the last element, but not every time), because it's not blocking the main
go routine. To make use of proper values for each closures, we need to copy the proper value to use. For example, you can do this:
func main() {
var wg sync.WaitGroup
for _, url := range urls {
fmt.Println("correct: " + url)
url2 = url
wg.Add(1)
go func() {
fmt.Println("wrong: " + url2)
wg.Done()
}()
}
wg.Wait()
}
Or can do what's already in the previous answer, which is more explicit and readable:
func main() {
var wg sync.WaitGroup
for _, url := range urls {
fmt.Println("correct: " + url)
wg.Add(1)
go func(url string) {
fmt.Println("wrong: " + url)
wg.Done()
}(url)
}
wg.Wait()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论