初始通道Channel

  • Golang语言最有特色的语言类型,与goroutine共同代表Go语言独有的并发编程模式和编程哲学
  • 不要使用共享内存来通信,而应该通过同性来共享内存(Rob Pike的至理名言)
  • 通道类型的值本身就是并发安全的(Go语言中唯一可以满足并发安全性的类型)

通道的特性

  • 对同一通道,发送操作之间是互斥的,接收操作之间也是互斥的
  • 发送操作和接收操作中对元素值的处理都是不可分割的
  • 发送操作在完全完成之前会被阻塞,接收操作也是如此
  • 进入通道并不是接收符右边的元素值,而是它的副本
  • 移出通道与上述类似

发送操作和接收操作什么时候可能会长时间阻塞

  • 对于缓冲通道, 如果通道已满,那么对它的所有发送操作都会被阻塞,直到通道中有元素值被接收
  • 对于缓冲通道,如果通道为空,那么对它的接收操作都会被阻塞,直到通道中有新的元素出现
  • 对于非缓冲通道,无论是发送操作还是接收操作,一开始执行都会被阻塞,直到配对的操作也开始执行,才会继续传递
  • 非缓冲通道是在用同步的方式传递数据,缓冲通道则是用异步的放肆传递数据
  • 由于通道是引用类型,所以它的零值就是nil
  • 只声明该类型的变量但没有用make函数对它进行初始化时,该变量的值就会是nil(因此千万不要初始化通道)

哪些操作会引发通道的panic

  • 已经初始化,但未关闭的通道,接收和发送操作一定不会引发panic
  • 通道一旦关闭,在对它进行发送操作都会引发panic
  • 对于nil值的通道,对于它的接收和发送操作都会引发panic
  • 为了防止意外的panic,千万不要让接收方关闭通道,而应该让发送方做这件事
  1. package main
  2. import "fmt"
  3. func main() {
  4. ch1 := make(chan int, 2)
  5. // 发送方。
  6. go func() {
  7. for i := 0; i < 10; i++ {
  8. fmt.Printf("Sender: sending element %v...\n", i)
  9. ch1 <- i
  10. }
  11. fmt.Println("Sender: close the channel...")
  12. close(ch1)
  13. }()
  14. // 接收方。
  15. for {
  16. elem, ok := <-ch1
  17. if !ok {
  18. fmt.Println("Receiver: closed channel")
  19. break
  20. }
  21. fmt.Printf("Receiver: received an element: %v\n", elem)
  22. }
  23. fmt.Println("End.")
  24. }

通道的方向

双向通道

即可发也可收的通道

单向通道

  • chan <- : 只能用来收
  • <- chan :只能用来发


单向通道的主要用途用于约束代码的行为**

  • 通过设置接口中函数通道的类型
  1. type Notifier interface { SendInt(ch chan<- int)}

取出通道中的值

使用for range 循环的取出通道中的值

  1. ch := make(chan int, 3)
  2. ch <- 1
  3. ch <- 2
  4. ch <- 3
  5. for elem := range ch {
  6. fmt.Printf("The element in ch: %v\n", elem)
  7. }

使用 select 语言

  • 如果有默认分支,无论通道表达式是否有阻塞,select语句都不会阻塞
  • 如果没有默认分支,那么一旦所有的case表达式都没有满足取值条件,那么select语句就会被阻塞
  • 如通道关闭了,可以从通道中直接接受一个其元素类型的零值。这时可以使用第二个参数来判断(boolean类型)
  1. func main() {
  2. intChan := make(chan int, 1)
  3. time.AfterFunc(time.Second, func() {
  4. close(intChan)
  5. })
  6. select {
  7. case _, ok := <-intChan:
  8. if !ok {
  9. fmt.Println("The candidate case is closed.")
  10. break
  11. }
  12. fmt.Printf("The candidate case is selected.")
  13. }
  14. }