goroutine之间通信的管道——channel

Goroutine 介绍

goroutine 其实就是线程,但是它比线程更小,十几个 goroutine 可能体现在底层就是五六个线程,而且Go语言内部也实现了 goroutine 之间的内存共享。

go 关键字可以创建 goroutine,将 go 声明放到一个需调用的函数之前,在相同地址空间调用运行这个函数,这样该函数执行时便会作为一个独立的并发线程,这种线程在Go语言中则被称为 goroutine。

具体创建代码如下:

//go 关键字放在方法调用前新建一个 goroutine 并执行方法体
go GetThingDone(param1, param2);

//新建一个匿名方法并执行
go func(param1, param2) {
}(val1, val2)

//直接新建一个 goroutine 并在 goroutine 中执行代码块
go {
    //do someting...
}

channel介绍

channel 是Go语言在语言级别提供的 goroutine 间的通信方式。我们可以使用 channel 在两个或多个 goroutine 之间传递消息。

channel 是类型相关的,也就是说,一个 channel 只能传递一种类型的值,这个类型需要在声明 channel 时指定。

定义一个 channel 时,也需要定义发送到 channel 的值的类型,注意,必须使用 make 创建 channel,代码如下所示:

ch1 := make(chan int)                 // 创建一个整型类型的通道
ch2 := make(chan interface{})         // 创建一个空接口类型的通道, 可以存放任意格式

type Equip struct{ /* 一些字段 */ }
ch2 := make(chan *Equip)             // 创建Equip指针类型的通道, 可以存放*Equip

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

channel特性

Go语言中的通道(channel)是一种特殊的类型。在任何时候,同时只能有一个 goroutine 访问通道进行发送和获取数据。goroutine 间通过通道就可以通信。
通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。

使用通道发送数据

通道的发送使用特殊的操作符<-,将数据通过通道发送的格式为:
通道变量 <- 值

通道变量:通过make创建好的通道实例。
值:可以是变量、常量、表达式或者函数返回值等。值的类型必须与ch通道的元素类型一致。

具体例子代码如下:

// 创建一个空接口通道
ch := make(chan interface{})
// 将0放入通道中
ch <- 0
// 将hello字符串放入通道中
ch <- "hello"

把数据往通道中发送时,如果接收方一直都没有接收,那么发送操作将持续阻塞。

使用通道接收数据

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

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

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

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

  1. 阻塞接收数据
    阻塞模式接收数据时,将接收变量作为<-操作符的左值,格式如下:
    data := <-ch
    执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量。

  2. 非阻塞接收数据
    使用非阻塞方式从通道接收数据时,语句不会发生阻塞,格式如下:
    data, ok := <-ch
    data:表示接收到的数据。未接收到数据时,data 为通道类型的零值。
    ok:表示是否接收到数据。
    非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。如果需要实现接收超时检测,可以配合 select 和计时器 channel 进行,可以参见后面的内容。

  3. 接收任意数据,忽略接收的数据
    阻塞接收数据后,忽略从通道返回的数据,格式如下:
    <-ch
    执行该语句时将会发生阻塞,直到接收到数据,但接收到的数据会被忽略。这个方式实际上只是通过通道在 goroutine 间阻塞收发实现并发同步。

  4. 循环接收
    通道的数据接收可以借用 for range 语句进行多个元素的接收操作,格式如下:
    for data := range ch {}
    通道 ch 是可以进行遍历的,遍历的结果就是接收到的数据。数据类型就是通道的数据类型。通过 for 遍历获得的变量只有一个,即上面例子中的 data。

你可能感兴趣的:(go的学习之路)