当我运行goroutines时,我通常得到40作为值,我知道它的并发性,但为什么最后一个数字会出现?我想输出必须是:
Page number: 34
Page number: 12
Page number: 8
Page number: 2
Page number: 29
示例源代码:
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func getWebPageContent(url string, c chan int, val int) interface{} {
if r, err := http.Get(url); err == nil {
defer r.Body.Close()
if body, err := ioutil.ReadAll(r.Body); err == nil {
c <- val
return string(body)
}
} else {
fmt.Println(err)
}
return "XoX"
}
const MAX_TH = 40
func main() {
// pln := fmt.Println
messages := make(chan int)
for j := 0; j < MAX_TH; j++ {
go func() { getWebPageContent("http://www.example.com", messages, j) }()
}
routine_count := 0
var page_number int
for {
page_number = <-messages
routine_count++
fmt.Println("Page number: ", page_number)
if routine_count == MAX_TH {
break
}
}
close(messages)
}
答案 0 :(得分:5)
Go编程语言
Frequently Asked Questions (FAQ)
What happens with closures running as goroutines?
使用并发闭包时可能会出现一些混淆。 请考虑以下程序:
func main() { done := make(chan bool) values := []string{"a", "b", "c"} for _, v := range values { go func() { fmt.Println(v) done <- true }() } // wait for all goroutines to complete before exiting for _ = range values { <-done } }
有人可能错误地期望看到a,b,c作为输出。你要做什么 可能看到的是c,c,c。这是因为每次迭代都是如此 loop使用变量v的相同实例,因此每个闭包共享 那个单变量。当闭包运行时,它会打印v的值 在fmt.Println执行时,但v可能已被修改 自从goroutine发布以来。帮助检测这个和其他 问题发生之前,请去看兽医。
在启动时将v的当前值绑定到每个闭包,一个 必须修改内部循环以在每次迭代时创建一个新变量。 一种方法是将变量作为参数传递给闭包:
for _, v := range values { go func(u string) { fmt.Println(u) done <- true }(v) }
在此示例中,v的值作为参数传递给 匿名功能。然后可以在函数内访问该值 作为变量u。
更简单的方法就是使用声明创建一个新变量 可能看似奇怪但在Go中工作正常的风格:
for _, v := range values { v := v // create a new 'v'. go func() { fmt.Println(v) done <- true }() }
因此,在您的情况下,通过添加语句j := j
,
for j := 0; j < MAX_TH; j++ {
j := j
go func() { getWebPageContent("http://www.example.com", messages, j) }()
}
例如,
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func getWebPageContent(url string, c chan int, val int) interface{} {
if r, err := http.Get(url); err == nil {
defer r.Body.Close()
if body, err := ioutil.ReadAll(r.Body); err == nil {
c <- val
return string(body)
}
} else {
fmt.Println(err)
}
return "XoX"
}
const MAX_TH = 40
func main() {
// pln := fmt.Println
messages := make(chan int)
for j := 0; j < MAX_TH; j++ {
j := j
go func() { getWebPageContent("http://www.example.com", messages, j) }()
}
routine_count := 0
var page_number int
for {
page_number = <-messages
routine_count++
fmt.Println("Page number: ", page_number)
if routine_count == MAX_TH {
break
}
}
close(messages)
}
输出:
Page number: 23
Page number: 6
Page number: 1
Page number: 3
Page number: 28
Page number: 32
Page number: 18
Page number: 22
Page number: 0
Page number: 36
Page number: 7
Page number: 21
Page number: 12
Page number: 2
Page number: 5
Page number: 4
Page number: 33
Page number: 13
Page number: 20
Page number: 27
Page number: 29
Page number: 8
Page number: 31
Page number: 10
Page number: 17
Page number: 25
Page number: 19
Page number: 35
Page number: 14
Page number: 38
Page number: 15
Page number: 30
Page number: 37
Page number: 39
Page number: 26
Page number: 9
Page number: 16
Page number: 11
Page number: 24
Page number: 34
答案 1 :(得分:1)
我的第一个golang回复,可能完全关闭: - )
循环可能看起来像这样:
...
for j := 0; j < MAX_TH; j++ {
go func(x) { getWebPageContent("http://www.example.com", messages, x) }(j)
}
...
基本上,您定义一个匿名函数并使用参数调用它。你可以采用不同的方式,但这个解决方案看起来非常实用和时尚: - )