Golang并发问题

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

Golang Concurrency Issue

问题

我正在学习Golang并发,并编写了一个程序来按顺序显示URL。我期望代码返回以下结果:
http://bing.com***
http://google.com***

但实际上它总是返回http:/google.com***。好像变量被覆盖了。由于我使用了goroutine,我希望它同时返回两个值。

func check(u string) string {
    tmpres := u+"*****"
    return tmpres
}

func IsReachable(url string) string {
    ch := make(chan string, 1)
    go func() { 
        ch <- check(url) 
    }()
    select {
    case reachable := <-ch:
        // 使用错误和回复
        return reachable
    case <-time.After(3 * time.Second):
        // 调用超时
        return "none"
    }
}

func main() {
    var urls = []string{
        "http://bing.com/",
        "http://google.com/",
    }

    for _, url := range urls {
        go func() {
            fmt.Println(IsReachable(url)) 
        }()
    }
    time.Sleep(1 * time.Second)
}

请注意,由于goroutine的特性,循环中的匿名函数会共享相同的url变量。为了解决这个问题,你可以将url作为参数传递给匿名函数:

for _, url := range urls {
    go func(u string) {
        fmt.Println(IsReachable(u)) 
    }(url)
}

这样每个goroutine都会使用自己的url值,而不会被覆盖。

英文:

I am learning Golang concurrency and have written a program to display URL's in order. I expect the code to return
http://bing.com***
http://google.com***

But it always returns http:/google.com*** . As if the variable is being overwritten.Since i am using goroutines i would expect it to return both values at the sametime.

func check(u string) string {
tmpres := u+&quot;*****&quot;
return tmpres
}

func IsReachable(url string) string {
ch := make(chan string, 1)
go func() { 

    ch &lt;- check(url) 

     }()
select {
case reachable := &lt;-ch:
    // use err and reply
    return reachable
case &lt;-time.After(3* time.Second):
    // call timed out
    return &quot;none&quot;
}
   }



func main() {

var urls = []string{
  &quot;http://bing.com/&quot;,
  &quot;http://google.com/&quot;,
}

for _, url := range urls {
    go func() {
     fmt.Println(IsReachable(url)) 
     }()
}
time.Sleep(1 * time.Second)
  }

答案1

得分: 5

两个问题。首先,你创建了一个竞态条件。通过闭包引用循环变量,你在循环线程和 goroutine 线程之间共享了该变量,这导致了你描述的问题:当为第一个 URL 启动的 goroutine 尝试运行时,变量的值已经改变。你需要将其复制到一个局部变量中,或者将其作为参数传递,例如:

for _, url := range urls {
    go func(url string) {
     fmt.Println(IsReachable(url)) 
     }(url)
}

其次,你说你想要按顺序显示它们,这与并发/并行一般不兼容,因为你无法控制并行操作的顺序。如果你想要按顺序执行它们,应该在单个线程中按顺序执行。否则,你将不得不收集结果,等待它们全部返回,然后按照所需的顺序对结果进行排序,然后再打印它们。

英文:

Two problems. First, you've created a race condition. By closing over the loop variable, you're sharing it between the thread running the loop and the thread running the goroutine, which is causing your described problem: by the time the goroutine that was started for the first URL tries to run, the value of the variable has changed. You need to either copy it to a local variable, or pass it as an argument, e.g.:

for _, url := range urls {
    go func(url string) {
     fmt.Println(IsReachable(url)) 
     }(url)
}

Second, you said you wanted to display them "in order", which is not a goal generally compatible with concurrency/parallism, because you cannot control the order of parallel operations. If you want them in order, you should do them in order in a single thread. Otherwise, you'll have to collect the results, wait for all them to come back, then sort the results back into the desired order before printing them.

huangapple
  • 本文由 发表于 2017年8月18日 00:56:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/45741018.html
匿名

发表评论

匿名网友

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

确定