通道是用来传递数据的一个数据结构。
通道可用于两个goroutine之间通过传递一个指定类型的值来同步运行和通讯。操作符<-用于指定通道的方向,发送或者接受。如果未指定方向,则为双向通道。
ch <- v // 把v发送到通道chv := <- ch // 从ch通道接收数据,并把值赋给v
声明通道,使用chan关键字接口,通道在使用前必须先创建
ch := make(chan int)
注意:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接受数据。
以下实例通过两个goroutine来计算数字之和,在goroutine完成计算后,它会计算两个结果的和:
package mainimport "fmt"func sum(s []int, c chan int) {sum := 0for _, v := range s {sum += v}c <- sum // 把sum发送到通道c}func main() {s := []int{7, 2, 8, -9, 4, 0}c := make(chan int)go sum(s[:len(s)/2], c)go sum(s[len(s)/2:], c)x, y := <-c, <-c //从通道c中接受fmt.Println(x, y, x+y)}
输出结果为 -5 17 12 ????为什么不是17 -5 12
通道缓冲区
通道可以设置缓冲区,通过make的第二个参数指定缓冲区大小ch := make(chan int, 100)
带缓冲区的通道允许 发送端的 数据发送 和 接收端的 数据获取 处于异步状态
由于缓冲区的大小有限,所以还是必须有接收端来接收数据,否则缓冲区一满,数据发送端就无法在发送数据
注意:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。
package mainimport "fmt"func main() {// 这里我们定义了一个可以存储整数类型的带缓冲通道// 缓冲区大小为2ch := make(chan int, 2)// 因为 ch 是带缓冲的通道,我们可以同时发送两个数据// 而不用立刻需要去同步读取数据ch <- 1ch <- 2// 获取这两个数据fmt.Println(<-ch)fmt.Println(<-ch)}
遍历通道与关闭通道
通过range关键字来实现遍历读取到的数据,类似于数组或切片,格式如下v, ok := <- ch
如果通道接受不到数据后,ok就为false,这时通道可以使用close()函数来关闭
package mainimport "fmt"func fibonacci(n int, c chan int) {x, y := 0, 1for i := 0; i < n; i++ {c <- xx, y = y, x+y}close(c)}func main() {c := make(chan int, 10)go fibonacci(cap(c), c)// range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个// 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据// 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不// 会结束,从而在接收第 11 个数据的时候就阻塞了。for i := range c {fmt.Println(i)}}
0112358132134
