29

Golang | 简介channel常见用法,完成goroutin通信

 3 years ago
source link: http://www.cnblogs.com/techflow/p/13586000.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

今天是 golang专题 的第14篇文章,大家可以点击上方的专辑回顾之前的内容。

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

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

channel

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

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

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

我们先来看它的使用,首先是定义一个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 main

import "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在其中扮演的角色其实就是队列。

生产者往队列当中传输数据,消费者进行消费,唯一不同的是这个队列的容量是1,必须要生产和消费端都准备就绪了才会进行数据传输。

chan的缓冲

前文说了,chan的容量只有1,只有消费端和生产端都就绪的时候才可以传输数据。我们也可以给chan 加上缓冲 ,如果消费端来不及把所有的数据都消费完,允许生产端先把数据暂时存在chan当中,先不发生阻塞,只有在chan满了之后才会阻塞。

用法也很简单,我们在通过make创建chan的时候多加上一个参数表示容量即可,和我们之前创建切片的道理很类似。

Ch := make(chan int, 100)

比如这样,我们就创建了一个缓冲区为100的信道。

但多说一句,其实这种情况不太常用,原因也很简单。因为 上下游的消费情况是统一的 ,如果生产者生产的速度过快,而消费端跟不上的话,即使把它先暂存在缓冲区当中也没什么用,早晚还是会要阻塞的。

当我们对信道使用结束之后,可以通过close语句将它关闭。

Close这个操作 只能在生产端进行 ,消费端如果close信道会引发一个panic。我们在从chan接收数据的时候,可以加上一个参数判断信道是否关闭。

v, ok := <- ch
if !ok {
return
}

这样我们就可以判断chan关闭的时间了。

今天的文章到这里就结束了,如果喜欢本文的话,请来一波 素质三连 ,给我一点支持吧( 关注、转发、点赞 )。

相关阅读

Python | 面试的常客,经典的生产消费者模式

原文链接,求个关注

- END -


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK