go中的chan是动态的,因此千万不要把chan想象成slice切片类型的数据。
- 只要chan不close可以永远发送数据和接受数据
 - 如果channel里面没有数据,接收方会阻塞
 - 如果没有人正在等待channel的数据,发送方会阻塞
 - 从一个close的channel取数据永远不会阻塞, 同时获取的数据为默认值(与定义的chan类型一致)
 
下面这段代码,会解释上面这四个chan特点:
package main
import (
    "net/http"
    "fmt"
    "sync"
)
func printUrl(url string) {
    res, err := http.Get(url)
    if err != nil{
        fmt.Println(err)
        return
    }
    defer res.Body.Close()
    fmt.Println(url, res.Status)
}
func work2(urls chan string, wg *sync.WaitGroup)  {
    for{
        url, ok := <- urls      // 判断取出的数据是否为false 说明chan关闭
        if !ok{
            break
        }
        printUrl(url)
    }
    wg.Done()
}
func work(urls chan string, wg *sync.WaitGroup) {
    for i := range urls{        // chan中有一条数据会循环一次知道chan关闭
        printUrl(i)
    }
    wg.Done()
}
func main() {
    url := "http://www.baidu.com"
    var u []string
    for i :=0; i<15;i ++{   // 先生成一些数据
        u = append(u, url)
    }
    wg := new(sync.WaitGroup)   // 利用WaitGroup的方式协程同步
    //wg.Add(5)                 // 可一次性添加任务总数
    urlchann := make(chan string)
    for i:=0; i<5 ;i++  {
        wg.Add(1)           // 也可每创建一个任务 添加一个标识
        go work(urlchann, wg)
    }
    for _, line := range u{
        urlchann <- line        // 将任务需要的数据添加到队列中
    }
    close(urlchann)         // 关闭chan
    wg.Wait()
}
代码中的Work与Work2效果是一样的。