duanqin9507 2016-02-08 05:25
浏览 29
已采纳

用频道奇怪的结果去goroutine

When i run the goroutines, i generally get 40 as value, i know its about the concurrency but why is the last number coming? I suppose the output must be:

Page number:  34  
Page number:  12  
Page number:  8  
Page number:  2  
Page number:  29

example source code:

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)
}
  • 写回答

2条回答 默认 最新

  • doukun8944 2016-02-08 06:55
    关注

    The Go Programming Language

    Frequently Asked Questions (FAQ)

    What happens with closures running as goroutines?

    Some confusion may arise when using closures with concurrency. Consider the following program:

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

    One might mistakenly expect to see a, b, c as the output. What you'll probably see instead is c, c, c. This is because each iteration of the loop uses the same instance of the variable v, so each closure shares that single variable. When the closure runs, it prints the value of v at the time fmt.Println is executed, but v may have been modified since the goroutine was launched. To help detect this and other problems before they happen, run go vet.

    To bind the current value of v to each closure as it is launched, one must modify the inner loop to create a new variable each iteration. One way is to pass the variable as an argument to the closure:

    for _, v := range values {
        go func(u string) {
            fmt.Println(u)
            done <- true
        }(v)
    }
    

    In this example, the value of v is passed as an argument to the anonymous function. That value is then accessible inside the function as the variable u.

    Even easier is just to create a new variable, using a declaration style that may seem odd but works fine in Go:

    for _, v := range values {
        v := v // create a new 'v'.
        go func() {
            fmt.Println(v)
            done <- true
        }()
    }
    

    Therefore, in your case, create a new variable by adding the statement j := j,

    for j := 0; j < MAX_TH; j++ {
        j := j
        go func() { getWebPageContent("http://www.example.com", messages, j) }()
    }
    

    For example,

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

    Output:

    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条)

报告相同问题?

悬赏问题

  • ¥20 MIT控制器能控制不稳定系统吗
  • ¥15 公司代码X对业务伙伴X无效,处理方法?
  • ¥15 微信内链接跳转到浏览器打开怎么实现
  • ¥15 三角波可以直接加施密特电路整形到矩形波吗实物
  • ¥15 html,php,在使用html请求php文件时发生了错误,无法请求到php文件读取数据库并用javascript进行数据显示,刷新
  • ¥15 touchsocket udp组播
  • ¥20 MAC怎么安装Silverlight 插件?以及安装了怎么启用
  • ¥15 VS2012中查询语句无法填入解析,数值传不进去
  • ¥15 gis系统开发出现命名空间“ESRI.ArcGIS”中不存在类型或命名空间名“Analyst3D”报错
  • ¥15 怎么让ai定时给我发信息 c#或者python