Golang程序挂起而未完成执行

I have the following golang program;

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 
", 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

However when I run it; it hangs (ie the last part that ought to print Im done doesnt run.) Here's the terminal output;;
$ go run get.go
start
Fetching http://yahoo.com/
Fetching http://golang.org/
Fetching http://www.google.com/
sent to chan
&{http://www.google.com/ 0xc820144120 fetched}
sent to chan
&{http://golang.org/ 0xc82008b710 fetched}
sent to chan
&{http://yahoo.com/ 0xc82008b7a0 fetched}

The problem is that ranging over a channel in a for loop will continue forever unless the channel is closed. If you want to read precisely len(urls) values from the channel, you should loop that many times:

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

Another good dirty devious trick would be to use sync.WaitGroup and increment it per goroutine and then monitor it with a Wait and after its done it will close your channel allowing the next blocks of code to run, the reason I am offering you this approach is because it gets away from using a static number in a loop like len(urls) so that you can have a dynamic slice that might change and what not.

The reason Wait and close are in their own goroutine is so that your code can reach the for loop to range over your channel

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 
", 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")

}