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循环