通道是用来传递数据的一个数据结构。
通道可用于两个goroutine之间通过传递一个指定类型的值来同步运行和通讯。操作符<-用于指定通道的方向,发送或者接受。如果未指定方向,则为双向通道。

  1. ch <- v // 把v发送到通道ch
  2. v := <- ch // 从ch通道接收数据,并把值赋给v

声明通道,使用chan关键字接口,通道在使用前必须先创建

  1. ch := make(chan int)

注意:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接受数据。
以下实例通过两个goroutine来计算数字之和,在goroutine完成计算后,它会计算两个结果的和:

  1. package main
  2. import "fmt"
  3. func sum(s []int, c chan int) {
  4. sum := 0
  5. for _, v := range s {
  6. sum += v
  7. }
  8. c <- sum // 把sum发送到通道c
  9. }
  10. func main() {
  11. s := []int{7, 2, 8, -9, 4, 0}
  12. c := make(chan int)
  13. go sum(s[:len(s)/2], c)
  14. go sum(s[len(s)/2:], c)
  15. x, y := <-c, <-c //从通道c中接受
  16. fmt.Println(x, y, x+y)
  17. }

输出结果为 -5 17 12 ????为什么不是17 -5 12

通道缓冲区

通道可以设置缓冲区,通过make的第二个参数指定缓冲区大小
ch := make(chan int, 100)
带缓冲区的通道允许 发送端的 数据发送 和 接收端的 数据获取 处于异步状态
由于缓冲区的大小有限,所以还是必须有接收端来接收数据,否则缓冲区一满,数据发送端就无法在发送数据
注意:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。

  1. package main
  2. import "fmt"
  3. func main() {
  4. // 这里我们定义了一个可以存储整数类型的带缓冲通道
  5. // 缓冲区大小为2
  6. ch := make(chan int, 2)
  7. // 因为 ch 是带缓冲的通道,我们可以同时发送两个数据
  8. // 而不用立刻需要去同步读取数据
  9. ch <- 1
  10. ch <- 2
  11. // 获取这两个数据
  12. fmt.Println(<-ch)
  13. fmt.Println(<-ch)
  14. }

遍历通道与关闭通道

通过range关键字来实现遍历读取到的数据,类似于数组或切片,格式如下
v, ok := <- ch
如果通道接受不到数据后,ok就为false,这时通道可以使用close()函数来关闭

  1. package main
  2. import "fmt"
  3. func fibonacci(n int, c chan int) {
  4. x, y := 0, 1
  5. for i := 0; i < n; i++ {
  6. c <- x
  7. x, y = y, x+y
  8. }
  9. close(c)
  10. }
  11. func main() {
  12. c := make(chan int, 10)
  13. go fibonacci(cap(c), c)
  14. // range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个
  15. // 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据
  16. // 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不
  17. // 会结束,从而在接收第 11 个数据的时候就阻塞了。
  18. for i := range c {
  19. fmt.Println(i)
  20. }
  21. }
  1. 0
  2. 1
  3. 1
  4. 2
  5. 3
  6. 5
  7. 8
  8. 13
  9. 21
  10. 34