Channels也可以用于将多个goroutine连接在一起,一个Channel的输出作为下一个Channel的输入。这种串联的Channels就是所谓的管道(pipeline)。下面的程序用两个channels将三个goroutine串联起来:

第一个goroutine是一个计数器,用于生成0、1、2、……形式的整数序列,然后通过channel将该整数序列发送给第二个goroutine;第二个goroutine是一个求平方的程序,对收到的每个整数求平方,然后将平方后的结果通过第二个channel发送给第三个goroutine;第三个goroutine是一个打印程序,打印收到的每个整数。为了保持例子清晰,我们有意选择了非常简单的函数,当然三个goroutine的计算很简单,在现实中确实没有必要为如此简单的运算构建三个goroutine。

func main() {
naturals := make(chan int)
squares := make(chan int) // Counter
go func() {
for x := 0; ; x++ {
naturals <- x
}
}() // Squarer
go func() {
for {
x := <-naturals
squares <- x * x
}
}() // Printer (in main goroutine)
for {
fmt.Println(<-squares)
}
}

  

如您所料,上面的程序将生成0、1、4、9、……形式的无穷数列。像这样的串联Channels的管道(Pipelines)可以用在需要长时间运行的服务中,每个长时间运行的goroutine可能会包含一个死循环,在不同goroutine的死循环内部使用串联的Channels来通信。但是,如果我们希望通过Channels只发送有限的数列该如何处理呢?

如果发送者知道,没有更多的值需要发送到channel的话,那么让接收者也能及时知道没有多余的值可接收将是有用的,因为接收者可以停止不必要的接收等待。这可以通过内置的close函数来关闭channel实现:

close(naturals)

  

当一个channel被关闭后,再向该channel发送数据将导致panic异常。当一个被关闭的channel中已经发送的数据都被成功接收后,后续的接收操作将不再阻塞,它们会立即返回一个零值。

关闭上面例子中的naturals变量对应的channel并不能终止循环,它依然会收到一个永无休止的零值序列,然后将它们发送给打印者goroutine。

没有办法直接测试一个channel是否被关闭,但是接收操作有一个变体形式:它多接收一个结果,多接收的第二个结果是一个布尔值ok,ture表示成功从channels接收到值,false表示channels已经被关闭并且里面没有值可接收。使用这个特性,我们可以修改squarer函数中的循环代码,当naturals对应的channel被关闭并没有值可接收时跳出循环,并且也关闭squares对应的channel.

// Squarer
go func() {
for {
x, ok := <-naturals
if !ok {
break // channel was closed and drained
}
squares <- x * x
}
close(squares)
}()

  

因为上面的语法是笨拙的,而且这种处理模式很常见,因此Go语言的range循环可直接在channels上面迭代。使用range循环是上面处理模式的简洁语法,它依次从channel接收数据,当channel被关闭并且没有值可接收时跳出循环。

在下面的改进中,我们的计数器goroutine只生成100个含数字的序列,然后关闭naturals对应的channel,这将导致计算平方数的squarer对应的goroutine可以正常终止循环并关闭squares对应的channel。(在一个更复杂的程序中,可以通过defer语句关闭对应的channel。)最后,主goroutine也可以正常终止循环并退出程序。

func main() {
naturals := make(chan int)
squares := make(chan int) // Counter
go func() {
for x := 0; x < 100; x++ {
naturals <- x
}
close(naturals)
}() // Squarer
go func() {
for x := range naturals {
squares <- x * x
}
close(squares)
}() // Printer (in main goroutine)
for x := range squares {
fmt.Println(x)
}
}

  

其实你并不需要关闭每一个channel。只有当需要告诉接收者goroutine,所有的数据已经全部发送时才需要关闭channel。不管一个channel是否被关闭,当它没有被引用时将会被Go语言的垃圾自动回收器回收。(不要将关闭一个打开文件的操作和关闭一个channel操作混淆。对于每个打开的文件,都需要在不使用的时候调用对应的Close方法来关闭文件。)

试图重复关闭一个channel将导致panic异常,试图关闭一个nil值的channel也将导致panic异常。关闭一个channels还会触发一个广播机制。

Goroutines和Channels(五)的更多相关文章

  1. 如果这种方式导致程序明显变慢或者引起其他问题,我们要重新思考来通过 goroutines 和 channels 来解决问题

    https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/09.3.md 9.3 锁和 sync 包 在一些复杂的程序中,通常通 ...

  2. Goroutines和Channels

    原文链接 https://golangbot.com/goroutines/ Goroutines Goroutines 可以被认为是多个函数或方法同时允许.可以认为是一个轻量级的线程.与线程的花费相 ...

  3. Goroutines和Channels(四)

    如果说goroutine是Go语言程序的并发体的话,那么channels则是它们之间的通信机制. 一个channel是一个通信机制,它可以让一个goroutine通过它给另一个goroutine发送值 ...

  4. Goroutines和Channels(三)

    clock服务器每一个连接都会起一个goroutine.在本节中我们会创建一个echo服务器,这个服务在每个连接中会有多个goroutine.大多数echo服务仅仅会返回他们读取到的内容,就像下面这个 ...

  5. Goroutines和Channels(二)

    网络编程是并发大显身手的一个领域,由于服务器是最典型的需要同时处理很多连接的程序,这些连接一般来自于彼此独立的客户端. 本小节,我们会讲解go语言的net包,这个包提供编写一个网络客户端或者服务器程序 ...

  6. Goroutines和Channels(一)

    Go语言中的并发程序可以用两种手段来实现.本章讲解goroutine和channel,其支持“顺序通信进程”(communicating sequential processes)或被简称为CSP.C ...

  7. ARTS-S golang goroutines and channels(二)

    向tcp服务端发消息 package main import ( "io" "log" "net" "os" ) fun ...

  8. ARTS-S golang goroutines and channels(一)

    先用golang实现一个简单的tcp服务端,假定文件名为clock1.go // clock1.go package main import ( "fmt" "io&qu ...

  9. 我学习go的五个感悟(译)

    我学习go的五个感悟(译) 原文 5 things about programming I learned with Go By MICHAŁ KONARSKI Go在最近一段时间内开始变得十分流行. ...

随机推荐

  1. Andrew Ng-ML-第十九章-应用举例:照片OCR(光学字符识别)

    1.问题描述与 OCR pipeline 图1.图像文字识别流水线 首先是输入图片->进行文字检测->字符分割->字符识别. 这些阶段分别需要1-5人这样子. 2.滑动窗口 主要讲滑 ...

  2. openstack 部署笔记--nova

    控制节点 配置用户与服务 $ . admin-openrc $ openstack user create --domain default --password-prompt nova $ open ...

  3. 【介绍+安装】Nginx的介绍和安装详解

    == 介绍和安装 == Nginx是一个自由.开源.高性能及轻量级的HTTP服务器及反转代理服务器, 其性能与IMAP/POP3代理服务器相当.Nginx以其高性能.稳定.功能丰富.配置简单及占用系统 ...

  4. HDU 4770

    这题说的是一在一个N*M的房间内,然后有些房间不能被灯光照亮,有一个灯可以转动方向,其他的灯只能在固定一个方向上,因为数据比较小,所以比较水,直接暴力的进行枚举就好了,但是还是 wa了很久,原因没认真 ...

  5. Description Resource Path LocationType Java compiler level does not match the version of the instal

    从别的地方导入进来的maven项目报: Description Resource Path Location TypeJava compiler level does not match the ve ...

  6. 查找nginx安装的路径以及相关安装操作命令

    查找nginx安装的路径以及相关安装操作命令 Linux环境下,怎么确定Nginx是以那个config文件启动的? [root@localhost ~]# ps -ef | grep nginxroo ...

  7. SpringBoot——定时任务+WebSocket(问题)

    开发环境:win7 + idea2018 + jdk 1.8 + springboot 2.x 记一次出现问题,我在项目中先集成了websocket环境,并且测试通过,之后想要模拟实时推送的效果,虽然 ...

  8. JUC原子类 1

    根据修改的数据类型,可以将JUC包中的原子操作类可以分为4类. 1. 基本类型: AtomicInteger, AtomicLong, AtomicBoolean ; 2. 数组类型: AtomicI ...

  9. Ubuntu系统下在github中新增库的方法

    上一篇介绍了Ubuntu16.04系统下安装git的方法.本博客介绍怎么在github上怎么新建库. 如图 root@ranxf:/home/ranxf/learnGit/ranran_jiekou# ...

  10. bzoj1639 / P2884 [USACO07MAR]每月的费用Monthly Expense

    P2884 [USACO07MAR]每月的费用Monthly Expense 二分经典题 二分每个段的限制花费,顺便统计下最大段 注意可以分空段 #include<iostream> #i ...