Go基础系列:双层channel用法示例
Go channel系列:
双层通道的解释见Go的双层通道
以下是一个双层通道的使用示例。注意下面的示例中使用了"信号通道"(Signal channel),但这里的信号通道是多余的,仅仅只是为了介绍。
信号通道不用来传递数据,而是用来传递消息,用来产生可读、可写的事件,以便让select选中某个分支。产生消息事件的方式有多种,比如直接关闭通道、发送false/true布尔值等等。
package main
import (
"fmt"
"time"
)
func main() {
// 定义双层通道cc
cc := make(chan chan int)
times := 5
for i := 1; i < times+1; i++ {
// 定义信号通道f
f := make(chan bool)
// 每次循环都在双层通道cc中生成内层通道c
// 并通过信号通道f来终止f1()
go f1(cc, f)
// 从双层通道cc中取出内层通道ch
// 并向ch通道发送数据
ch := <-cc
ch <- i
// 从ch中取出数据
for sum := range ch {
fmt.Printf("Sum(%d)=%d\n", i, sum)
}
// 每个循环睡眠一秒钟
time.Sleep(time.Second)
// 每次循环都关闭信号通道f
close(f)
}
}
// 双层通道cc用来生成内层通道c
// 并使用信号通道f来终止函数f1()
func f1(cc chan chan int, f chan bool) {
c := make(chan int)
cc <- c
defer close(c)
sum := 0
select {
// 从内层通道中取出数据,计算和,然后发回内层通道
case x := <-c:
for i := 0; i <= x; i++ {
sum = sum + i
}
// goroutine将阻塞在此,直到数据被读走
c <- sum
// 信号通道f可读时,结束f1()的运行
// 但因为select没有在for中,该case分支用不上
case <-f:
return
}
}
上面的示例中,函数f1()两个参数,一个是双层通道cc,一个是信号通道f。f1()中首先生成了一个通道c,并发送给了双层通道cc,使得main()中可以从cc中取得这个内层通道c,并向其发送数据。
回到f1()中,select最初会被阻塞,因为内层通道c和信号通道f都没有数据可读。由于main()可以取得内层通道c,并向其发送数据,使得f1()中的select第一个case分支被选中,该分支会计算发送的整数之前的总和,并将计算结果重新发送给内层通道c,让main()可以取得这个计算结果。
上面的示例中有几个细节需要注意:
- 在f1()中必须关闭内层通道c,因为main()中的range迭代一个未关闭的通道会一直阻塞,而且每次调用f1()都会重新创建c通道。
- 上面的信号通道其实没有起到任何作用。
- f1()中的select必须不能放进for循环。因为f1()将数据发回c之后,如果在for中,发f()所在的goroutine将阻塞在select上,由于c通道还没有关闭,这会导致main goroutine因range迭代操作而阻塞,也就是说所有goroutine都被阻塞了,出现了死锁。
所以,当在select中有发送操作的时候,很可能会出现死锁现象。这时,要么为select加上default,要么为select加上超时时间,要么select不要放在for循环中。
Go基础系列:双层channel用法示例的更多相关文章
- Go基础系列:nil channel用法示例
Go channel系列: channel入门 为select设置超时时间 nil channel用法示例 双层channel用法示例 指定goroutine的执行顺序 当未为channel分配内存时 ...
- Go基础系列:channel入门
Go channel系列: channel入门 为select设置超时时间 nil channel用法示例 双层channel用法示例 指定goroutine的执行顺序 channel基础 chann ...
- Go基础系列:WaitGroup用法说明
正常情况下,新激活的goroutine的结束过程是不可控制的,唯一可以保证终止goroutine的行为是main goroutine的终止.也就是说,我们并不知道哪个goroutine什么时候结束. ...
- Go基础系列:为select设置超时时间
Go channel系列: channel入门 为select设置超时时间 nil channel用法示例 双层channel用法示例 指定goroutine的执行顺序 After() 谁也无法保证某 ...
- Go基础系列:指定goroutine的执行顺序
Go channel系列: channel入门 为select设置超时时间 nil channel用法示例 双层channel用法示例 指定goroutine的执行顺序 当关闭一个channel时,会 ...
- C#基础系列——多线程的常见用法详解
前言:前面几节分别介绍了下C#基础技术中的反射.特性.泛型.序列化.扩展方法.Linq to Xml等,这篇跟着来介绍下C#的另一基础技术的使用.最近项目有点紧张,所以准备也不是特别充分.此篇就主要从 ...
- C#基础系列——委托实现简单设计模式
前言:上一篇介绍了下多线程的相关知识:C#基础系列——多线程的常见用法详解,里面就提到了委托变量.这篇简单介绍下委托的使用.当然啦,园子里面很多介绍委托的文章都会说道:委托和事件的概念就像一道坎,过了 ...
- C#基础系列——一场风花雪月的邂逅:接口和抽象类
前言:最近一个认识的朋友准备转行做编程,看他自己边看视频边学习,挺有干劲的.那天他问我接口和抽象类这两个东西,他说,既然它们如此相像, 我用抽象类就能解决的问题,又整个接口出来干嘛,这不是误导初学者吗 ...
- c#基础系列(转)
转:http://www.cnblogs.com/landeanfen/p/4953025.html C#基础系列——一场风花雪月的邂逅:接口和抽象类 前言:最近一个认识的朋友准备转行做编程,看他自己 ...
随机推荐
- tp5自定义分页参数
代码示例: $data = db('activity') -> where($condition1)-> order('startline desc') -> paginate(2, ...
- Aes加解密,php
Aes类库 <?php namespace Aes; class Aes { /** * var string $method 加解密方法,可通过openssl_get_cipher_metho ...
- CentOS开启telnet服务
原文地址:https://blog.csdn.net/zhujiangm/article/details/4540778 服务器:CentOS-5.1(192.168.1.87)客户机:FC6(192 ...
- Docker构建其它组件
构建mysql 运行centos7容器 docker run --privileged -dti --name=centos-container centos:7 /usr/sbin/init 查询c ...
- vue数据更新UI不刷新显示解决方案
vue比较常见的坑就是数据(后台返回)更新了,但是UI界面并没有更新,常见于以下情况: 一.数据为数组时1.通过数组索引修改数组元素例如: 此时UI数据并不会刷新 2.修改数组长度时: 解决方案: 如 ...
- java 基础复习
最近准备阿里巴巴的面试,被提到需要一个比较好的java能力,因此花了点时间,再次把JAVA看了一遍,其中的某些重点记录下来,以便以后复习. (1)& 和&& 的区别 (2)排序 ...
- Android中监控home键
一.需求 在应用开发过程中,启动服务开启线程锁等待服务返回解锁,为了避免点击home键使线程锁卡死的bug,需要监控home键,解锁线程. 二.实现 在应用时,需要register和unregiste ...
- 背水一战 Windows 10 (121) - 后台任务: 推送通知
[源码下载] 背水一战 Windows 10 (121) - 后台任务: 推送通知 作者:webabcd 介绍背水一战 Windows 10 之 后台任务 推送通知 示例演示如何接收推送通知/WebA ...
- Android插件化的兼容性(中):Android P的适配
Android系统的每次版本升级,都会对原有代码进行重构,这就为插件化带来了麻烦. Android P对插件化的影响,主要体现在两方面,一是它重构了H类中Activity相关的逻辑,另一个是它重构了I ...
- win7中调试Hotspot
预置: 1.Visual Studio 2010 2.CYGWIN(设置到path中) 3.jdk(设置到path中) 4.Framework4(系统如果有就不需要安装) 5.win7 sdk 7.1 ...