Golang程序挂起而没有完成执行

时间:2015-12-05 17:54:36

标签: go channel goroutine

我有以下golang程序;

package main

import (
    "fmt"
    "net/http"
    "time"
)

var urls = []string{
    "http://www.google.com/",
    "http://golang.org/",
    "http://yahoo.com/",
}

type HttpResponse struct {
    url      string
    response *http.Response
    err      error
    status   string
}

func asyncHttpGets(url string, ch chan *HttpResponse) {
    client := http.Client{}
    if url == "http://www.google.com/" {
        time.Sleep(500 * time.Millisecond) //google is down
    }

    fmt.Printf("Fetching %s \n", url)
    resp, err := client.Get(url)
    u := &HttpResponse{url, resp, err, "fetched"}
    ch <- u
    fmt.Println("sent to chan")
}

func main() {
    fmt.Println("start")
    ch := make(chan *HttpResponse, len(urls))
    for _, url := range urls {
        go asyncHttpGets(url, ch)
    }

    for i := range ch {
        fmt.Println(i)
    }
    fmt.Println("Im done")

}

Run it on Playground

然而,当我运行它;它挂起(即应该打印Im done的最后一部分没有运行。) 这是终端输出;;
$ go run get.go
启动
获取http://yahoo.com/
获取http://golang.org/
获取http://www.google.com/
发给了chan &amp; {http://www.google.com/ 0xc820144120取得了} 发给了chan &amp; {http://golang.org/ 0xc82008b710取了}
发给了chan &amp; {http://yahoo.com/ 0xc82008b7a0 fetched}

2 个答案:

答案 0 :(得分:5)

问题是,除非关闭通道,否则for循环中的通道范围将永远持续。如果要从通道中精确读取len(urls)值,则应循环多次:

for i := 0; i < len(urls); i++ {
    fmt.Println(<-ch)
}

答案 1 :(得分:0)

另一个好的狡猾的狡猾技巧是使用sync.WaitGroup并按goroutine递增它,然后用Wait进行监控,完成后它会关闭你的频道,允许下一个块要运行的代码,我之所以提供这种方法,是因为它在len(urls)之类的循环中不再使用静态数字,因此您可以拥有一个可能会发生变化的动态切片。

Waitclose在他们自己的goroutine中的原因是,您的代码可以通过您的频道到达for looprange

package main

import (
    "fmt"
    "net/http"
    "time"
    "sync"
)

var urls = []string{
    "http://www.google.com/",
    "http://golang.org/",
    "http://yahoo.com/",
}

type HttpResponse struct {
    url      string
    response *http.Response
    err      error
    status   string
}

func asyncHttpGets(url string, ch chan *HttpResponse, wg *sync.WaitGroup) {
    client := http.Client{}
    if url == "http://www.google.com/" {
        time.Sleep(500 * time.Millisecond) //google is down
    }

    fmt.Printf("Fetching %s \n", url)
    resp, err := client.Get(url)
    u := &HttpResponse{url, resp, err, "fetched"}
    ch <- u
    fmt.Println("sent to chan")
    wg.Done()
}

func main() {
    fmt.Println("start")
    ch := make(chan *HttpResponse, len(urls))
    var wg sync.WaitGroup
    for _, url := range urls {
        wg.Add(1)
        go asyncHttpGets(url, ch, &wg)
    }

    go func() {
        wg.Wait()
        close(ch)
    }()

    for i := range ch {
        fmt.Println(i)
    }
    fmt.Println("Im done")

}