通道是用来传递数据的一个数据结构。
通道可用于两个goroutine之间通过传递一个指定类型的值来同步运行和通讯。操作符<-用于指定通道的方向,发送或者接受。如果未指定方向,则为双向通道。
ch <- v // 把v发送到通道ch
v := <- ch // 从ch通道接收数据,并把值赋给v
声明通道,使用chan关键字接口,通道在使用前必须先创建
ch := make(chan int)
注意:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接受数据。
以下实例通过两个goroutine来计算数字之和,在goroutine完成计算后,它会计算两个结果的和:
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, 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 main
import "fmt"
func main() {
// 这里我们定义了一个可以存储整数类型的带缓冲通道
// 缓冲区大小为2
ch := make(chan int, 2)
// 因为 ch 是带缓冲的通道,我们可以同时发送两个数据
// 而不用立刻需要去同步读取数据
ch <- 1
ch <- 2
// 获取这两个数据
fmt.Println(<-ch)
fmt.Println(<-ch)
}
遍历通道与关闭通道
通过range关键字来实现遍历读取到的数据,类似于数组或切片,格式如下v, ok := <- ch
如果通道接受不到数据后,ok就为false,这时通道可以使用close()函数来关闭
package main
import "fmt"
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, 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)
}
}
0
1
1
2
3
5
8
13
21
34