【Go】Golang Channel介绍

一. Channel简介

Channel 是Golang的2大核心之一,类似Linux的管道,为并发Goroutine提供一种同步通信机制,借助于Channel不同的Goroutine之间可以相互通信。

  1. 创建channel: make(chan type) type表示具体数据类型,除了支持常规的int、float64、string等类型外,还支持struct、interface等
    ch1 := make(chan int)
    ch2 := make(chan float64)
    ch3 := make(chan string)
    ch4 := make(chan struct{})
    ch5 := make(chan interface{})
    
  2. 发送数据: ch <- vch <- ch在左边表示发送数据到channel ch中
  3. 接收数据: x := <- ch<- ch ch在右边表示从channel ch中接收数据

根据发送和接收数据,可以分成以下3类Channel

  1. 可以发送&可以接收: make(chan type),默认创建channel都是可以发送和接收数据
  2. 只可以发送: make(chan<- type),只可以发送数据channel
  3. 只可以接收: make(<-chan type),只可以接收数据channel

和slice类似,channel在make的时候也可以设置队列容量,根据队列容量大小,可以分成以下2类Channel

  1. 无缓冲channel: make没有设置队列容量或者队列容量为0,表示channel是一个无缓存channel,例如make(chan type)
  2. 缓冲channel: make设置队列容量不为0,表示channel是一个缓存channel,例如make(chan type, size)

channel可以被关闭,可以使用内置的close函数关闭一个channel。

channel具有以下几个特点

  1. 从一个nil channel接收数据会一直阻塞
    var c1 chan int
    go func() {
        for {
            v := <- c1
        fmt.Println(v)  //一直阻塞直到程序退出
        }
    }()
    
    time.Sleep(5 * time.Second)
    fmt.Println("exit ~")
    
  2. 如果channel没有数据,只要channel未关闭,从channel中接收数据会一直阻塞直到有数据为止
    // 无缓冲channel
    c1 := make(chan int)
    go func() {
         <- c1  //阻塞直到c1有数据
         fmt.Println("receiver from c1")
    }()
    
    fmt.Println(time.Now().Unix())
    time.Sleep(2* time.Second)
    fmt.Println(time.Now().Unix())
    c1 <- 1
    time.Sleep(5* time.Second)
    fmt.Println("exit ~")
    
    // 有缓冲channel
    c2 := make(chan int, 100)
    go func() {
         <- c2  //阻塞直到c2有数据
         fmt.Println("receiver from c2")
    }()
    
    fmt.Println(time.Now().Unix())
    time.Sleep(2* time.Second)
    fmt.Println(time.Now().Unix())
    c2 <- 1
    time.Sleep(5* time.Second)
    fmt.Println("exit ~")
    
  3. 无缓冲channel,发送数据和接收数据同时发生。如果没有receiver接收数据(<- chan),则sender发送数据会一直阻塞;如果没有sender发送数据(chan <-),则receiver接收数据会一直阻塞
    // 没有receiver接收数据
    c1 := make(chan int)
    go func() {
        c1 <- 1  //阻塞直到有receiver
        fmt.Println("sender 1 to c1")
    }()
    time.Sleep(20 * time.Second)
    fmt.Println("exit ~")
    
    // 没有sender发送数据
    c2 := make(chan int)
    go func() {
        <- c2   //阻塞直到有sender
        fmt.Println("receiver from c2")
    }()
    time.Sleep(20 * time.Second)
    fmt.Println("exit ~")
    
  4. 有缓冲channel,当队列容器未满的时候sender不会阻塞,当队列容量满的时候sender会阻塞,当队列容量为空的时候receiver会阻塞
    c1 := make(chan int, 10)
    go func() {
        for i := 0; i < 100; i++ {
            c1 <- i
        fmt.Printf("send %d to c1\n", i)   //i为10的时候开始阻塞,直到receiver开始消费
        }
    }()
    
    time.Sleep(5 * time.Second)
    fmt.Println("sleep 5 seconds done ~")
    go func() {
        for {
            <- c1   //消费完所有数据后,receiver开始阻塞
        }
    }()
    
    time.Sleep(20 * time.Second)
    fmt.Println("exit ~")
    
  5. 关闭nil channel会panic (panic: close of nil channel)
    var c1 chan int
    close(c1)  //panic: close of nil channel
    fmt.Println("exit ~")
    
  6. 重复关闭channel会panic (panic: close of closed channel)
    c1 := make(chan int)
    close(c1)
    time.Sleep(5 * time.Second)
    close(c1)  //panic: close of closed channel
    fmt.Println("exit ~")
    
  7. 向已经关闭channel发送数据会panic (panic: send on closed channel)
    c1 := make(chan int)
    close(c1)
    time.Sleep(5 * time.Second)
    c1 <- 1   //panic: send on closed channel
    fmt.Println("exit ~")
    
  8. 从已经关闭的channel读取数据不会阻塞,如果channel为空读到对应类型默认值,例如int默认值是0,指针默认值为nil
    c1 := make(chan int, 5)
    go func() {
        for i := 1; i <= 5; i++ {
        c1 <- i
        }
    }()
    time.Sleep(5 * time.Second)
    
    close(c1)
    
    go func() {
        for {
            v := <- c1  
        fmt.Println(v)  //打印出 1 2 3 4 5 0 0 0 0 ...
        }
    }()
    
    time.
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值