GO Channel

1 不缓存的channel

以最简单方式调用make函数创建的时一个无缓存的channel,但是我们也可以指定第二个整形参数,对应channel的容量。如果channel的容量大于零,那么该channel就是带缓存的channel

ch = make(chan int)    // unbuffered channel
ch = make(chan int, 0) // unbuffered channel


一个基于无缓存Channels的发送操作将导致发送者goroutine阻塞,直到另一个goroutine在相同的Channels上执行接收操作,当发送的值通过Channels成功传输之后,两个goroutine可以继续执行后面的语句。


反之,如果接收操作先发生,那么接收者goroutine也将阻塞,


直到有另一个goroutine在相同的Channels上执行发送操作


func gosum(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    c <- sum // send sum to c
}
    var si []int = []int{1, 2, 3, 4}
    chan_c := make(chan int, 0)
    go gosum(si, chan_c)
    rs := <-chan_c
    fmt.Println(rs)



2 缓存的channel

带缓存的Channel内部持有一个元素队列。通过缓存的使用,可以尽量避免阻塞,提供应用的性能。队列的最大容量是在调用make函数创建channel时通过第二个参数指定的,比如

ch = make(chan int, 3) // buffered channel with capacity 3


我们可以在无阻塞的情况下连续向新创建的channel发送三个值


ch <- "A"
ch <- "B"
ch <- "C"


此刻,channel的内部缓存队列将是满的,如果有第四个发送操作将发生阻塞。channel的缓存队列解耦了接收和发送的goroutine

3 channel & Range

    c := make(chan int)
    go func() {
        for i := 0; i < 10; i = i + 1 {
            c <- i
            time.Sleep(1 * time.Second)
        }
        close(c)
    }()
    for i := range c {
        fmt.Println(i)
    }
    time.Sleep(100 * time.Second)
range c 产生的迭代值为Channel中发送的值,它会一直迭代知道channel被关闭。上面的例子中如果把 close(c) 注释掉,程序会一直阻塞在 for …… range 那一行

4 单向channel

Go语言的类型系统提供了单方向的channel类型,分别用于只发送或只接收的channel。类型chan<- int表示一个只发送int的channel,只能发送不能接收。相反,类型<-chan int表示一个只接收int的channel,只能接收不能发送。(箭头<-和关键字chan的相对位置表明了channel的方向。)这种限制将在编译期检测。

因为关闭操作只用于断言不再向channel发送新的数据,所以只有在发送者所在的goroutine才会调用close函数,因此对一个只接收的channel调用close将是一个编译错误

func gosum(s []int, c chan<- int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    c <- sum // send sum to c
}

5 channel&goroutines应用-批量查询数据库

func Query(conns []Conn, query string) Result {
    ch := make(chan Result, len(conns))  // buffered
    for _, conn := range conns {
        go func(c Conn) {
            ch <- c.DoQuery(query):
        }(conn)
    }
    return <-ch
}

6 select

select 语句选择一组可能的send操作和receive操作去处理。它类似 switch ,但是只是用来处理通讯(communication)操作。它的 case 可以是send语句,也可以是receive语句,亦或者 default 。receive 语句可以将值赋值给一个或者两个变量。它必须是一个receive操作。最多允许有一个 default case ,它可以放在case列表的任何位置,尽管我们大部分会将它放在最后。

如果有同时多个case去处理,比如同时有多个channel可以接收数据,那么Go会伪随机的选择一个case处理(pseudo-random)。如果没有case需要处理,则会选择 default 去处理,如果 default case 存在的情况下。如果没有 default case ,则 select 语句会阻塞,直到某个case需要处理

select 语句和 switch 语句一样,它不是循环,它只会选择一个case来处理,如果想一直处理channel,你可以在外面加一个无限的for循环