基本介绍

  • channel本质就是一个数据结构——队列
  • 数据是先进先出
  • 线程安全,多 goroutine 访问时,不需要加锁
  • channel是有类型的(string,int,interface 类型断言)

image.png

定义声明channel

  1. var 变量名 chan 数据类型
  2. var intChan chan int // intChan 用于存放 int 数据
  3. var mapChan chan map[int]string // mapChan 用于存放 map[int]string 类型数据
  4. var perChan chan Person // 结构体
  5. var perChan chan *Person // 结构体指针
  1. channel 是引用类型
  2. channel 必须初始化才能写入数据,即 make 后才能使用
  3. channel 是有类型的, (比如: intChan 只能写入 int 类型数据)
  4. channel 中的数据取完之后,再继续取就会报死锁: fatal error: all goroutines are asleep - deadlock!

代码

  1. package main
  2. import "fmt"
  3. func main() {
  4. // 1、声明 chan 类型的数据,存放 3 个int
  5. var intChan chan int
  6. fmt.Println("intChan =", intChan) // nil
  7. // 2、初始化
  8. intChan = make(chan int, 3)
  9. // intChan类型: chan int, 值: 0xc00001c100 , 本身的地址: 0xc000006028
  10. fmt.Printf("intChan类型: %T, 值: %v , 本身的地址: %v\n", intChan, intChan, &intChan)
  11. // 3、向管道(channel)写入数据
  12. // 注意: 写入数据时,不能超过其容量,fatal error: all goroutines are asleep - deadlock!
  13. // 致命错误:所有goroutine都处于休眠状态-死锁!
  14. intChan <- 10
  15. num := 211
  16. intChan <- num
  17. intChan <- 12
  18. // 4、查看channel的长度和容量
  19. fmt.Printf("intChan长度: %v, 容量: %v \n", len(intChan), cap(intChan))
  20. // 5、读取 channel - 队列(先进先出FIFO)
  21. var num2 int = <-intChan
  22. fmt.Println("num2 = ", num2)
  23. fmt.Printf("intChan长度: %v, 容量: %v \n", len(intChan), cap(intChan))
  24. // 6、在没有使用协程的情况下,如果我们的管道数据已经全部取出,再继续取就会报 deadlock (死锁)
  25. num3 := <-intChan
  26. num4 := <-intChan
  27. // num5 := <-intChan // channel 里已经没有数据,不能再取了 fatal error: all goroutines are asleep - deadlock!
  28. fmt.Println("num3 = ", num3, "num4 = ", num4)
  29. }

interface{} 类型的 channel

任何数据类型都实现了空接口,使用的时候要先断言数据类型

  1. package main
  2. import "fmt"
  3. type Cat struct {
  4. Name string
  5. Age int
  6. }
  7. func main() {
  8. var allChan chan interface{} = make(chan interface{}, 10)
  9. allChan <- 10
  10. cat1 := Cat{Name: "tom", Age: 2}
  11. cat2 := Cat{Name: "tom02", Age: 3}
  12. allChan <- cat1
  13. allChan <- cat2
  14. allChan <- "jack"
  15. // 取出
  16. item := <-allChan
  17. fmt.Printf("item类型:%T, 值: %v \n", item, item)
  18. // item类型:int, 值: 10
  19. // 类型断言
  20. newItem, ok := item.(Cat)
  21. fmt.Printf("newItem 类型:%T, 值: %v, ok = %v", newItem, newItem, ok)
  22. // newItem 类型:main.Cat, 值: { 0}, ok = false
  23. }

channel 的关闭和遍历

channel的关闭

使用内置函数close可以关闭channel,当channel关闭后,就不能再向channel写数据了,但是仍然可以从该channel读取数据。

channel的遍历

channel不能使用常规for循环遍历,因为每取一次数据,channel的长度就减1
channel支持for—range的方式进行遍历,请注意两个细节
1、在遍历时,如果channel没有关闭,则回出现 deadlock 的错误
2、在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历。

代码

  1. // channel 的关闭和遍历
  2. package main
  3. import "fmt"
  4. func main() {
  5. // 1、关闭
  6. intChan := make(chan int, 3)
  7. intChan <- 100
  8. intChan <- 200
  9. close(intChan)
  10. // intChan <- 300
  11. // 关闭之后不能写入 panic: send on closed channel
  12. fmt.Println("intChan = ", intChan) // intChan = 0xc000118080
  13. // 关闭之后可以读取
  14. n1 := <-intChan
  15. fmt.Println("n1 = ", n1) // n1 = 100
  16. // 2、遍历
  17. intChan2 := make(chan int, 100)
  18. for i := 1; i <= 100; i++ {
  19. intChan2 <- i // 放入100个数据
  20. }
  21. // 常规for循环只能遍历取出50个数据 - 因为每取一个,channel 的长度就 -1
  22. /* for i := 1; i <= len(intChan2); i++ {
  23. fmt.Printf("第%v个值:%v \n", i, <-intChan2)
  24. } */
  25. // for range 遍历, 没有 index
  26. close(intChan2)
  27. // 如果遍历时,没有关闭channel, 就会出现死锁 deadlock; 关闭后,则会正常遍历,遍历完之后,退出程序
  28. for v := range intChan2 {
  29. fmt.Println("v=", v)
  30. }
  31. }