美文网首页
什么是chan

什么是chan

作者: 乔大叶_803e | 来源:发表于2020-03-25 14:13 被阅读0次

如果说 goroutine 是 Go语言程序的并发体的话,那么 channels 就是它们之间的通信机制。一个 channels 是一个通信机制,它可以让一个 goroutine 通过它给另一个 goroutine 发送值信息。每个 channel 都有一个特殊的类型,也就是 channels 可发送数据的类型。一个可以发送 int 类型数据的 channel 一般写为 chan int。

Go语言提倡使用通信的方法代替共享内存,当一个资源需要在 goroutine 之间共享时,通道在 goroutine 之间架起了一个管道,并提供了确保同步交换数据的机制。声明通道时,需要指定将要被共享的数据的类型。可以通过通道共享内置类型、命名类型、结构类型和引用类型的值或者指针。

这里通信的方法就是使用通道(channel),如下图所示。


channel 和 goroutine的通信机制

在地铁站、食堂、洗手间等公共场所人很多的情况下,大家养成了排队的习惯,目的也是避免拥挤、插队导致的低效的资源使用和交换过程。代码与数据也是如此,多个 goroutine 为了争抢数据,势必造成执行的低效率,使用队列的方式是最高效的,channel 就是一种队列一样的结构。

通道的特性

Go语言中的通道(channel)是一种特殊的类型。在任何时候,同时只能有一个 goroutine 访问通道进行发送和获取数据。goroutine 间通过通道就可以通信。

通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。

声明通道类型

通道本身需要一个类型进行修饰,就像切片类型需要标识元素类型。通道的元素类型就是在其内部传输的数据类型,声明如下:

var 通道变量 chan 通道类型

  • 通道类型:通道内的数据类型。
  • 通道变量:保存通道的变量。

chan 类型的空值是 nil,声明后需要配合 make 后才能使用。

创建通道

通道是引用类型,需要使用 make 进行创建,格式如下:

通道实例 := make(chan 数据类型)

  • 数据类型:通道内传输的元素类型。
  • 通道实例:通过make创建的通道句柄。

请看下边的例子:

ch1 := make(chan int)                 // 创建一个整型类型的通道
ch2 := make(chan interface{})         // 创建一个空接口类型的通道, 可以存放任意格式
type Equip struct{ /* 一些字段 */ }
ch2 := make(chan *Equip)             // 创建Equip指针类型的通道, 可以存放*Equip

使用通道发送数据

通道创建后,就可以使用通道进行发送和接收操作。

  1. 通道发送数据的格式
    通道发送的特殊操作符 <-将数据通过通道发送格式:

通道变量 <- 值

  • 通道变量,通过make创建好通道实例
  • 值:可以是变量、常量、表达式或者是函数的返回值。值的类型必须与ch元素通道一致
  1. 通过通道发送数据的例子
    使用make创建一个通道后,就可以使用<- 向通道发送数据
// 创建一个空接口通道
ch := make(chan interface{})
// 将0放入通道中
ch <- 0
// 将hello字符串放入通道中
ch <- "hello"
  1. 发送将持续阻塞直到数据被接收

把数据往通道中发送时,如果接收方一直都没有接收,那么发送操作将持续阻塞。Go 程序运行时能智能地发现一些永远无法发送成功的语句并做出提示,代码如下:

package main
func main() {
    // 创建一个整型通道
    ch := make(chan int)
    // 尝试将0通过通道发送
    ch <- 0
}

运行代码,报错:

fatal error: all goroutines are asleep - deadlock!

报错的意思是:运行时发现所有的goroutine(包括main)都在等待goroutine,也就是说所有的goroutine中的channel并没有形成发送接收对应的代码。

使用通道接收数据

通道接收数据同样是使用<-操作符,通道接收有如下特性:
① 通道的收发操作在不同的两个 goroutine 间进行。
由于通道的数据在没有接收方处理时,数据发送方会持续阻塞,因此通道的接收必定在另外一个 goroutine 中进行。

② 接收将持续阻塞直到发送方发送数据。
如果接收方接收时,通道中没有发送方发送数据,接收方也会发生阻塞,直到发送方发送数据为止。

③ 每次接收一个元素。
通道一次只能接收一个数据元素。

通道的数据接收一共有以下 4 种写法。

  1. 阻塞接收数据
    阻塞模式接收数据时,将接收变量作为<-操作符的左值,格式如下:

data := <- ch

执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量。

  1. 非阻塞接收数据

data,ok := <- ch

  • data:表示接收到的数据。未接收到数据时,data 为通道类型的零值。
  • ok 表示是否接收到数据

非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。如果需要实现接收超时检测,可以配合 select 和计时器 channel 进行,可以参见后面的内容。

  1. 接收任意数据,忽略接收的数据
    阻塞接收数据后,忽略从通道返回的数据,格式如下:

<- ch

执行该语句时将会发生阻塞,直到接收到数据,但接收到的数据会被忽略。这个方式实际上只是通过通道在 goroutine 间阻塞收发实现并发同步。

  1. 循环接收
    通道的数据接收可以借用 for range 语句进行多个元素的接收操作,格式如下:
for data := range ch {
}

通道 ch 是可以进行遍历的,遍历的结果就是接收到的数据。数据类型就是通道的数据类型。通过 for 遍历获得的变量只有一个,即上面例子中的 data。

相关文章

  • 什么是chan

    如果说 goroutine 是 Go语言程序的并发体的话,那么 channels 就是它们之间的通信机制。一个 c...

  • Go中的chan

    go中的chan是动态的,因此千万不要把chan想象成slice切片类型的数据。 只要chan不close可以永远...

  • Go 基础 4:channel拔高 和 select

    先说这个buffered chan就是在创建chan的时候加上个chan buffer的大小 基本的chan在se...

  • Golang的通道简介以及通道的作用

    chanel通道,在golang的变量为chan,为什么要单独开一章出来讲解呢,主要是chan是一个挺重要的东西,...

  • Android 动态权限初探

    CHANGE LOG v0.1 2018/07/14 Chuck Chan 什么是 Android 动态权限 An...

  • Chan

    梦已入睡 渡人自渡 渡一生莲花 …… 十里桃花 待嫁的年华 悠悠风来 埋在三生石下 …… 倘若我心中的如来 你眼中...

  • chan

    1.主协程只负责调用子协程,但是不会被子协程阻塞 2.在主协程中,无缓冲的chan需要输入输出成对出现,否则会死锁...

  • chan

    chan 不带缓存 make(chan 数据类型) 进和出都会阻塞. 读和写同时存在,才会同时退出阻塞。如果只...

  • chan

    你不能每天这么chan我的 我心里咯噔了一下 原来我已经这么缠你了吗 你不喜欢我缠了啊 每次看着好吃的我又吃不到 ...

  • 书画禅---尹同君书法作品之一(中英双语)

    书画禅 Shu Hua Chan Chan Meditation with calligraphy and pai...

网友评论

      本文标题:什么是chan

      本文链接:https://www.haomeiwen.com/subject/ksstuhtx.html