Golang学习笔记(二十):深入学习Channel

627 阅读3分钟

原创作者,公众号【程序员读书】,欢迎关注公众号,转载文章请注明出处哦。

《Golang学习笔记(十九):Channel初步接触》小节中已经学习了channel的一些基础概念了,总结一下大概是以下几点:

  1. channel是Go语言中gotoutine之间数据通讯的机制。
  2. 可以使用内置函数make()初始化channel类型。
  3. 使用close()函数关闭channel关闭后,不能再往channel发送信息。

好了,简单复习了一下,下面开始深入的学习吧。

channel的分类

同步channel

当我们使用make()函数创建一下channel类型时,如下代码所示:

ch := make(chan int)

此时所创建的channel只能往里面发送一个元素,如果元素没有被另一个gotoutine接收,继续向channel发送元素的gotoutine便会阻塞,这种只能发送单个元素的channel,称为无缓存channel,也称为同步channel,可用于不同goroutine之间的数据同步操作。

package main
import "fmt"

func main() {
    ch1 := make(chan int)
    ch2 := make(chan int)
    // 计数
    go counter(ch1)
    // 求平方
    go squarer(ch1,ch2)
    // 在主goroutine中打印同步过来的数据
    for x := range ch2 {
        fmt.Println(x)
    }
}

func counter(ch chan int)  {
    for x := 0; x < 1000; x++ {
        ch <- x
    }
    close(ch)
}

func squarer(ch1,ch2 chan int){
    for x := range ch1 {
        ch2 <- x * x
    }
    close(ch2)
}

在上面的例子中,我们创建了两个goroutine,在counter goroutine产生数值,并同步到另一个squarer goroutine,而squarer goroutine则将元素同步到main goroutine当中,最后再打印输出。

这就是无缓存区的channel的作用,串联不同的goroutine,来达到数据同步的目的。

异步channel

如果想创建一个带缓存区的channel类型,可以使用make()函数的第二个参数来指定channel的队列容量,如:

ch := make(chan int,3)

通过这种创建的channel,也称为异步channel,底层数据结构为一个指定长度元素队列,这里的长度为3,当向channel发送数值时,如果队列未满,则不会发生阻塞,元素会添加在队列的末尾,而接收的goroutine则从队列的头部读取元素。

所以,根据channel是否有缓存区,可将channel分为同步channel异步channel

channel的方向性

在上面的例子中,我们看到squarer goroutinech1的操作只是读取元素,而对ch2操作只是发送元素,在counter goroutinech1的也只是发送元素。

如果从操作的方向性来讲的话,channel可分为只用于发送和只用于接收,也就是单方向channel,定义的方式如下所示:

var ch chan int //可以发送和接收的普通channel

var in <-chan int //只能发送操作的channel

var out chan<- int //只能接收操作的channel

当向用于接收的channel发送元素和从只能用于发送元素的channel接收元素时,都会引发错误。

另外,使用close()函数关闭只能用于接收的channel时,也会引发错误,因为只有发送端才能关闭。

因此,我们可以将上面的例子中两个函数的形参修改为:

func counter(ch chan<- int)  {
    ...//省略函数体
}

func squarer(ch1 <-chan int,ch2 chan<- int){
    ...//省略函数体
}

你的关注,是我写作路上最大的鼓励!