Go:匿名函数在 for range 中只接收最后一个值

huangapple go评论89阅读模式
英文:

Go: Anonymous Func Only Taking Last Value in For Range

问题

我想这是一个相当简单的修复。只需循环遍历字符串列表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("正确: " + 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()
}

huangapple
  • 本文由 发表于 2014年7月10日 23:51:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/24681010.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定