Goroutine 使用通道奇怪的结果

当我运行 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)

}


开满天机
浏览 185回答 2
2回答

慕无忌1623718

闭包作为 goroutine 运行会发生什么?在并发使用闭包时可能会出现一些混淆。考虑以下程序:func main() {&nbsp; &nbsp; done := make(chan bool)&nbsp; &nbsp; values := []string{"a", "b", "c"}&nbsp; &nbsp; for _, v := range values {&nbsp; &nbsp; &nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(v)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; done <- true&nbsp; &nbsp; &nbsp; &nbsp; }()&nbsp; &nbsp; }&nbsp; &nbsp; // wait for all goroutines to complete before exiting&nbsp; &nbsp; for _ = range values {&nbsp; &nbsp; &nbsp; &nbsp; <-done&nbsp; &nbsp; }}人们可能会错误地期望看到 a、b、c 作为输出。你可能会看到的是 c、c、c。这是因为循环的每次迭代都使用变量 v 的相同实例,因此每个闭包共享该单个变量。当闭包运行时,它会打印 fmt.Println 执行时 v 的值,但 v 可能在 goroutine 启动后被修改。为了帮助在这些问题和其他问题发生之前检测它们,请运行 go vet。要在启动时将 v 的当前值绑定到每个闭包,必须修改内部循环以在每次迭代时创建一个新变量。一种方法是将变量作为参数传递给闭包:for _, v := range values {&nbsp; &nbsp; go func(u string) {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(u)&nbsp; &nbsp; &nbsp; &nbsp; done <- true&nbsp; &nbsp; }(v)}在这个例子中,v 的值作为参数传递给匿名函数。然后可以在函数内部访问该值作为变量 u。更简单的方法是创建一个新变量,使用看起来很奇怪但在 Go 中工作正常的声明样式:for _, v := range values {&nbsp; &nbsp; v := v // create a new 'v'.&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(v)&nbsp; &nbsp; &nbsp; &nbsp; done <- true&nbsp; &nbsp; }()}因此,在您的情况下,通过添加语句创建一个新变量j := j,for j := 0; j < MAX_TH; j++ {&nbsp; &nbsp; j := j&nbsp; &nbsp; go func() { getWebPageContent("http://www.example.com", messages, j) }()}例如,package mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "io/ioutil"&nbsp; &nbsp; "net/http")func getWebPageContent(url string, c chan int, val int) interface{} {&nbsp; &nbsp; if r, err := http.Get(url); err == nil {&nbsp; &nbsp; &nbsp; &nbsp; defer r.Body.Close()&nbsp; &nbsp; &nbsp; &nbsp; if body, err := ioutil.ReadAll(r.Body); err == nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; c <- val&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return string(body)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(err)&nbsp; &nbsp; }&nbsp; &nbsp; return "XoX"}const MAX_TH = 40func main() {&nbsp; &nbsp; // pln := fmt.Println&nbsp; &nbsp; messages := make(chan int)&nbsp; &nbsp; for j := 0; j < MAX_TH; j++ {&nbsp; &nbsp; &nbsp; &nbsp; j := j&nbsp; &nbsp; &nbsp; &nbsp; go func() { getWebPageContent("http://www.example.com", messages, j) }()&nbsp; &nbsp; }&nbsp; &nbsp; routine_count := 0&nbsp; &nbsp; var page_number int&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; page_number = <-messages&nbsp; &nbsp; &nbsp; &nbsp; routine_count++&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("Page number: ", page_number)&nbsp; &nbsp; &nbsp; &nbsp; if routine_count == MAX_TH {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; close(messages)}输出:Page number:&nbsp; 23Page number:&nbsp; 6Page number:&nbsp; 1Page number:&nbsp; 3Page number:&nbsp; 28Page number:&nbsp; 32Page number:&nbsp; 18Page number:&nbsp; 22Page number:&nbsp; 0Page number:&nbsp; 36Page number:&nbsp; 7Page number:&nbsp; 21Page number:&nbsp; 12Page number:&nbsp; 2Page number:&nbsp; 5Page number:&nbsp; 4Page number:&nbsp; 33Page number:&nbsp; 13Page number:&nbsp; 20Page number:&nbsp; 27Page number:&nbsp; 29Page number:&nbsp; 8Page number:&nbsp; 31Page number:&nbsp; 10Page number:&nbsp; 17Page number:&nbsp; 25Page number:&nbsp; 19Page number:&nbsp; 35Page number:&nbsp; 14Page number:&nbsp; 38Page number:&nbsp; 15Page number:&nbsp; 30Page number:&nbsp; 37Page number:&nbsp; 39Page number:&nbsp; 26Page number:&nbsp; 9Page number:&nbsp; 16Page number:&nbsp; 11Page number:&nbsp; 24Page number:&nbsp; 34

慕码人8056858

我的第一个 golang 回复,可能完全关闭 :-)循环可能如下所示:...for j := 0; j < MAX_TH; j++ {&nbsp; &nbsp; go func(x) { getWebPageContent("http://www.example.com", messages, x) }(j)}...基本上,您定义一个匿名函数并使用参数调用它。你可以用不同的方式来做,但这个解决方案看起来非常实用和时尚:-)
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go