channel是什么意思(简介channel常见用法)

今天我们来看看golang当中另一个很重要的概念——信道 。我们之前介绍goroutine的时候曾经提过一个问题,当我们启动了多个goroutine之后,我们怎么样让goroutine之间保持通信呢?


要回答这个问题就需要用到信道 。


channel

信道的英文是channel,在golang当中的关键字是chan 。它的用途是用来在goroutine之间传输数据,这里你可能要问了,为什么一定得是goroutine之间传输数据呢,函数之间传递不行吗?


因为正常的传输数据直接以参数的形式传递就可以了,只有在并发场景当中,多个线程彼此隔离的情况下,才需要一个特殊的结构传输数据 。


Chan看起来比较怪,在其他语言当中基本没有出现过,但是它的原理和使用都非常简单 。


【channel是什么意思(简介channel常见用法)】我们先来看它的使用,首先是定义一个chan,还是老规矩,通过make关键字创建 。我们之前也提过,golang当中的一个设计原则就是能省则省,能简单则简单 。从这个make关键字就看得出来,它可以创建的东西太多了,既可以创建一个切片,也可以创建map,还可以创建信道 。


所以当我们要创建一个chan的时候,可以通过make实现 。


Ch := make(chan int)

我们在chan后面跟上一个类型,表示这个信道传输的数据类型 。如果你想要传输任何类型呢,那可以用我们之前说过的interface{} 。


Chan创建了之后,我们想要从其中获取数据或者是把数据放入其中也非常简单,简单到都没有api,直接用形象的传输语句就可以了 。


比如我们现在有一个chan是ch,我们想要放入数据,我们可以这样ch <- a 。我们想要从ch当中获取数据,我们可以v := <- ch 。


我们用箭头表示数据的流动,是不是很形象很直观呢?

阻塞

但是还没完,chan有一个很关键的点在于,chan的使用是阻塞的 。也就是说下游从chan当中拿走一个数据我们才可以传入一个数据 。否则的话,传输数据的代码就会一直等待chan清空 。


同样,如果我们定义了一个从chan当中读取数据的语句,假如当前的chan是空的话,那么它也会一直阻塞等待,直到chan当中有数据了为止 。


所以我们就知道了,chan的使用场景当中需要一个生产方,也需要一个消费方 。我们来看一个golang官方的一个例子:
package mainimport "fmt"func sum(s []int, c chan int) { sum := 0 for _, v := range s {  sum += v } c <- 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)}

我们启动了两个goroutine去对数组进行求和并进行返回,goroutine生产的数据是没办法直接return的,所以只能通过chan的形式传输出来 。chan传输出来需要下游消费,所以上面两个goroutine的数据会传输到x, y: <-c, <-c 这一句语句当中 。


前面说过了,chan的传输是阻塞的,所以这一句语句会一直等待,直到上面两个goroutine都计算完成了为止 。


如果你看的有些发蒙,觉得好似有些理解了又好似没有的话,那么很简单的一个办法是在理解的时候把这个使用场景做一个变幻 。把chan的使用场景想象成我们之前介绍过的生产者消费者设计模式,chan在其中扮演的角色其实就是队列 。