Go:匿名函数只在For范围内取最后一个值



我想这是一个相当容易的修复。只是尝试遍历字符串列表urls,并在循环迭代时打印出每个url。但是当我添加并发(goWaitGroup)时,它打印出来的是列表中最后一个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("correct: " + url)
        wg.Add(1)
        go func() {
            fmt.Println("wrong: " + url)
            wg.Done()
        }()
    }
    wg.Wait()
}

工作代码:

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()
}

也在go playground: http://play.golang.org/p/zFSDvfdIDS

添加我的答案,因为被接受的答案没有解释为什么。

Go中的闭包可以访问创建它的作用域的所有变量。因此,在下面的例子中创建的每个闭包都可以访问循环变量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()
}

因此,当每个闭包开始并发运行时,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()
}

最新更新