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. R中双表操作学习[转载]

    转自:https://www.jianshu.com/p/a7af4f6e50c3 1.原始数据 以上是原有的一个,再生成一个新的: > gene_exp_tidy2 <- data.fr ...

  2. AngularJS2.0起步

    ES6工具链 要让Angular2应用跑起来不是件轻松的事,因为它用了太多还不被当前主流浏览器支持的技术.所以,我们需要一个工具链:

  3. col-md-*和col-sm-*

    屏幕大于(≥992px) ,使用col-md-* 而不是col-sm-*如果屏幕大于(≥768px),小雨<=992px,使用col-sm-* 而不是col-md-*

  4. 机器学习理论基础学习13--- 隐马尔科夫模型 (HMM)

    隐含马尔可夫模型并不是俄罗斯数学家马尔可夫发明的,而是美国数学家鲍姆提出的,隐含马尔可夫模型的训练方法(鲍姆-韦尔奇算法)也是以他名字命名的.隐含马尔可夫模型一直被认为是解决大多数自然语言处理问题最为 ...

  5. ajax response 系统错误时responseText出现一堆代码

    在后期维护webform的一个项目时遇到个比较大的坑,前台ajax请求,失败时弹出后台自定义的错误信息responsetext.结果在本地运行时能正常弹出“验证码错误”,而发布到服务器上respons ...

  6. discuz完善用户资料任务不能完成的解决方法

    再来吐槽下discuz. 虽然很很很不想用discuz,但是,,,,..便利性以及各种原因,还得使用. 问题:discuz-运营-任务-完善资料,,,,,,变更部分后无法完成任务. 解决方式如下 原因 ...

  7. UVALive - 7261 Xiongnu's Land

    思路: 先二分下界,再二分上届. #include <bits/stdc++.h> using namespace std; #define MP make_pair #define PB ...

  8. jstack生成的Thread Dump日志结构解析

    1 第一部分:Full thread dump identifier 2 第二部分:Java EE middleware, third party & custom application T ...

  9. mustache多次渲染和多个赋值

    mustache多次渲染和多个赋值, html页面的script标签中的代码,设置多个键: <!-- 项目列表 --> <script type="text/x-templ ...

  10. sp3485推荐电路(转)

    源: sp3485推荐电路 注意:转自电子发烧友 转:485通信自动收发电路 转: RS485收发的3种典型电路-重点-自动收发电路