14.2.md 3.2 KB

14.2 协程间的信道

14.2.1 概念

在第一个例子中,协程是独立执行的,他们之间没有通信。他们必须通信才会变得更有用:彼此之间发送和接收信息并且协调/同步他们的工作。协程可以使用共享变量来通信,但是很不提倡这样做,因为这种方式给所有的共享内存的多线程都带来了困难。

而Go有一个特殊的类型,通道(channel),像是通道(管道),可以通过它们发送类型化的数据在协程之间通信,可以避开所有内存共享导致的坑;通道的通信方式保证了同步性。数据通过通道:同一时间只有一个协程可以访问数据:所以不会出现数据竞争,设计如此。数据的归属(可以读写数据的能力)被传递。

工厂的传送带是个很有用的例子。一个机器(生产者协程)在传送带上放置物品,另外一个机器(消费者协程)拿到物品并打包。

通道服务于通信的两个目的:值的交换,同步的,保证了两个计算(协程)任何时候都是可知状态。

通常使用这样的格式来声明通道:var identifier chan datatype

未初始化的通道的值是nil。

所以通道稚嫩传输一种类型的数据,比如chan int或者chan string,所有的类型都可以用于通道,空接口interface{}也可以。甚至可以(有时非常有用)创建通道的通道。

通道实际上是类型化消息的队列:使数据得以传输。它是先进先出(FIFO)结构的所以可以保证发送给他们的元素的顺序(有些人知道,通道可以比作Unix shells中的双向管道(tw-way pipe))。通道也是引用类型,所以我们使用make()函数来给它分配内存。这里先声明了一个字符串通道ch1,然后创建了它(实例化):

var ch1 chan string
ch1 = make(chan string)

当然可以更短: ch1 := make(chan string)

这里我们构建一个int通道的通道: chanOfChans := make(chan chan int)

或者函数通道: funcChan := chan func()(相关示例请看章节14.17

所以通道是对象的第一类型:可以存储在变量中,作为函数的参数传递,从函数返回以及通过通道发送它们自身。另外它们是类型化的,允许类型检查,比如尝试使用整数通道发送一个指针。

14.2.2 通信操作符 <-

这个操作符直观的标示了数据的传输:信息按照箭头的方向流动。

流向通道(发送)

ch <- int1表示:用通道ch发送变量int1(二进制操作符,中缀 = 发送)

从通道流出(接收),三种方式:

int2 = <- ch表示:变量int2从通道ch(一元运算的前缀操作符,前缀 = 接收)接收数据(获取新值);假设int2已经声明过了,如果没有的话可以写成:int2 := <- ch

<- ch可以单独调用获取通道的(下一个)值,当前值会被丢弃,但是可以用来验证,所以以下代码是合法的:

if <- ch != 1000{
...
}

链接