关于管道 Channel

  1. Channel 用来同步并发执行的函数并提供它们某种传值交流的机制。
  2. Channel 的一些特性:通过 channel 传递的元素类型、容器(或缓冲区)和 传递的方向由“<-”操作符指定。
  3. c <- 123,把值 123 输入到管道 c,<-c,把管道 c 的值读取到左边,value := <-c,这样就是读到 value 变量里面。

管道分类

无缓冲的 与 有缓冲 channel 有着重大差别,那就是一个是同步的(阻塞的) 一个是非同步的(非阻塞的)。

比如:

c1 := make(chan int)       // 无缓冲
c2 := make(chan int,1)     // 有缓冲

例如:c1 <- 1

  • 无缓冲: 不仅仅是向 c1 通道放 1,而是一直要等有别的协程 <-c1 接手了这个参数,那么 c1 <- 1 才会继续下去,要不然就一直阻塞着。
  • 有缓冲: c2 <- 1 则不会阻塞,因为缓冲大小是1(其实是缓冲大小为 0),只有当放第二个值,且第一个还没被人拿走的时候,才会阻塞。

例子 - 无缓冲

演示 无缓冲 和 有缓冲 的 channel 的样子

func test0() {/** 演示 无缓存 和 有缓冲 的 channel 的样子 */done := make(chan bool)       /** 无缓冲 */done1 := make(chan bool, 1)   /** 有缓冲 */println(done, done1)
}

演示 无缓冲在同一个 main 里面的 死锁例子

func test1() {/** 编译错误 deadlock,阻死 main 进程 *//** 演示 无缓冲在同一个 main 里面的 死锁例子 */done := make(chan bool)done <- true /** 这句是输入值,它会一直阻塞,等待读取 */<-done       /** 这句是读取,但是在上面已经阻死了,永远走不到这里 */println("完成")
}

演示仅有 输入 语句,但没 读取语句 的死锁例子

func test2() {/** 编译错误 deadlock,阻死 main 进程 *//** 演示仅有 输入 语句,但没 读取语句 的死锁例子 */done := make(chan bool)done <- true /** 输入,一直等待读取,哪怕没读取语句 */println("完成")
}

演示仅有 读取 语句,但没 输入语句 的死锁例子

func test3() {/** 编译错误 deadlock,阻死 main 进程 *//** 演示仅有 读取 语句,但没 输入语句 的死锁例子 */done := make(chan bool)<-done /** 读取输出,前面没有输入语句,done 是 empty 的,所以一直等待输入 */println("完成")
}

演示,协程的阻死,不会影响 main

func test4() {/** 编译通过 *//** 演示,协程的阻死,不会影响 main */done := make(chan bool)go func() {<-done /** 一直等待 */}()println("完成")/*** 控制台输出:*       完成*/
}

在 test4 的基础上,无缓冲 channel 在协程 go routine 里面阻塞死

func test5() {/** 编译通过 *//** 在 test4 的基础上,无缓冲 channel 在协程 go routine 里面阻塞死 */done := make(chan bool)go func() {println("我可能会输出哦") /** 阻塞前的语句 */done <- true       /** 这里阻塞死,但是上面那句有可能输出,见 test3 的结论 */println("我永远不会输出")<-done /** 这句也不会走到,除非在别的协程里面读取,或者在 main */}()println("完成")
}

编译通过,在 test5 的基础上演示,延时 main 的跑完

func test6() {/** 编译通过,在 test5 的基础上演示,延时 main 的跑完 */done := make(chan bool)go func() {println("我可能会输出哦")done <- true /** 这里阻塞死 */println("我永远不会输出")<-done /** 这句也不会走到 */}()time.Sleep(time.Second * 1) /** 加入延时 1 秒 */println("完成")/*** 控制台输出:*       我可能会输出哦*       完成*//*** 结论:*    如果在 go routine 中阻塞死,也可能不会把阻塞语句前的内容输出,*    因为main已经跑完了,所以延时一会,等待 go routine*/
}

演示无缓冲 channel 在 不同的位置里面接收填充和接收

func test7() {/** 编译通过,演示无缓冲channel 在 不同的位置里面 接收填充 和 接收*/done := make(chan bool)go func() {done <- true /** 直到,<-done 执行,否则这里阻塞死 */println("我永远不会输出,除非 <-done 执行")}()<-done /** 这里接收,在输出完成之前,那么上面的语句将会走通 */println("完成")/*** 控制台输出:*       我永远不会输出,除非 <-done 执行*       完成*/
}

演示无缓冲 channel 在不同地方接收的影响

func test8() {/** 编译通过,演示无缓冲 channel 在不同地方接收的影响 */done := make(chan bool)go func() {done <- true /** 直到,<-done 执行,否则这里阻塞死 */println("我永远不会输出,除非 <-done 执行")}()println("完成")<-done /** 这里接收,在输出完成之后 *//*** 控制台输出:*       完成*       我永远不会输出,除非 <-done 执行*/
}

无缓存的 channel 使用 close 后,不会阻塞

func test9() {/** 编译通过 *//** 演示,没缓存的 channel 使用 close 后,不会阻塞 */done := make(chan bool)close(done)// done<-true  /** 关闭了的,不能再往里面输入值,否则会 panic */<-done /** 这句是读取,但是在上面已经关闭 channel 了,不会阻死 */println("完成")
}

无缓存的 channel,在 go routine 里面使用 close 后,不会阻塞

func test10() {/** 编译通过 *//** 演示,没缓存的 channel,在 go routine 里面使用 close 后,不会阻塞 */done := make(chan bool)go func() {close(done)}()//done<-true  /** 关闭了的,不能再往里面输入值 */<-done /** 这句是读取,但是在上面已经关闭 channel 了,不会阻死 */println("完成")
}

例子 - 有缓冲

有缓冲的 channel 不会阻塞的例子

func test11() {/** 编译通过 *//** 有缓冲的 channel 不会阻塞的例子 */done := make(chan bool, 1)done <- true<-doneprintln("完成")
}

有缓冲的 channel 会阻塞的例子

func test12() {/** 编译通过 *//** 有缓冲的 channel 会阻塞的例子 */done := make(chan bool, 1)// done<-true /** 注释这句 */<-done /** 虽然是有缓冲的,但是在没输入的情况下,读取,会阻塞 */println("完成")
}

有缓冲的 channel 会阻塞的例子

func test13() {/** 编译不通过 *//** 有缓冲的 channel 会阻塞的例子 */done := make(chan bool, 1)done <- truedone <- false /** 放第二个值的时候,第一个还没被人拿走,这时候才会阻塞,根据缓冲值而定 */println("完成")
}

有缓冲的 channel 不会阻塞的例子

func test14() {/** 编译通过 *//** 有缓冲的 channel 不会阻塞的例子 */done := make(chan bool, 1)done <- true /** 不会阻塞在这里,等待读取 */println("完成")
}

有缓冲的 channel,如果在 go routine 中使用,一定要做适当的延时,否则会输出来不及,因为 main 已经跑完了,所以延时一会,等待 go routine

func test15() {/** 编译通过 *//** 有缓冲的channel 在 go routine 里面的例子 */done := make(chan bool, 1)go func() {/** 不会阻塞 */println("我可能会输出哦")done <- true /** 如果把这个注释,也会导致 <-done 阻塞 */println("我也可能会输出哦")<-doneprintln("别注释 done<-true 哦,不然我就输出不了了")}()time.Sleep(time.Second * 1) /** 1秒延时,去掉就可能上面的都不会输出也有可以输出,routine 调度 */println("完成")/*** 控制台输出:*       我可能会输出哦*       我也可能会输出哦*       完成*//*** 结论:*    有缓冲的 channel,如果在 go routine 中使用,一定要做适当的延时,否则会输出来不及,*    因为 main 已经跑完了,所以延时一会,等待 go routine*/
}

多 channel 模式

func getMessagesChannel(msg string, delay time.Duration) <-chan string {c := make(chan string)go func() {for i := 1; i <= 3; i++ {c <- fmt.Sprintf("%s %d", msg, i)time.Sleep(time.Millisecond * delay) /** 仅仅起到,下一次的 c 在何时输入 */}}()return c
}func test16() {/** 编译通过 *//** 复杂的演示例子 *//** 多 channel 模式 */c1 := getMessagesChannel("第一", 600)c2 := getMessagesChannel("第二", 500)c3 := getMessagesChannel("第三", 5000)/** 层层限制阻塞 *//** 这个 for 里面会造成等待输入,c1 会阻塞 c2 ,c2 阻塞 c3 *//** 所以它总是,先输出 c1 然后是 c2 最后是 c3 */for i := 1; i <= 3; i++ {/** 每次循环提取一轮,共三轮 */println(<-c1) /** 除非 c1 有输入值,否则就阻塞下面的 c2,c3 */println(<-c2) /** 除非 c2 有输入值,否则就阻塞下面的 c3 */println(<-c3) /** 除非 c3 有输入值,否则就阻塞进入下一轮循环,反复如此 */}/***  这个程序的运行结果,首轮的,第一,第二,第三 很快输出,因为*  getMessagesChannel 函数的延时 在 输入值之后,在第二轮及其之后*  因为下一个 c3 要等到 5 秒后才能输入,所以会阻塞第二轮循环的开始 5 秒,如此反复。*//** 修改:如果把 getMessagesChannel 里面的延时,放在输入值之前,那么 c3 总是等待 5秒 后输出 */
}

在 test16 基础修改的,复杂演示例,多 channel 的选择,延时在输入之后的情况

func test17() {/** 编译通过 *//** 在 test15 基础修改的,复杂演示例子 *//** 多 channel 的选择,延时在输入之后的情况 */c1 := getMessagesChannel("第一", 600)c2 := getMessagesChannel("第二", 500)c3 := getMessagesChannel("第三", 5000)/** 3x3 次循环,是 9 *//** select 总是会把最先完成输入的channel输出,而且,互不限制 *//** c1,c2,c3 每两个互不限制 */for i := 1; i <= 9; i++ {select {case msg := <-c1:println(msg)case msg := <-c2:println(msg)case msg := <-c3:println(msg)}}/*** 这个程序的运行结果:*    第二 1,第三 1,第一 1,第二 2,第一 2,第二 3,第一 3,第三 2,第三 3*//** 分析:前3次输出,“第一”,“第二”,“第三”,都有,而且*  是随机顺序输出,因为协程的调度,第 4,5,6 次,由于“第二”只延时 500ms,*  比 600ms 和 5000ms 都要小,那么它先输出,然后是“第一”,此时“第三”还不能输出,*  因为它还在等 5 秒。此时已经输出 5 次,再过 500ms,"第三"的 5 秒还没走完,所以继续输出"第一",*  再过 100ms,500+100=600,"第二"也再完成了一次,那么输出。至此,"第一"和"第二"已经*  把管道的 3 个值全部输出,9-7 = 2,剩下两个是 "第三"。此时,距离首次的 5000ms 完成,*  还有,500-600-600 = 3800ms,达到后,"第三" 将输出,再过 5 秒,最后一次"第三输出"*/
}

转载于:https://www.cnblogs.com/52php/p/6906983.html

[Go] 通过 17 个简短代码片段,切底弄懂 channel 基础相关推荐

  1. mysql建立电影表_【代码片段】MySQL新建表添加基础字段

    MySQL数据库表中,经常会用到的三个字段 SQL建表时,直接插入 gmt_create datetime null comment '创建时间', gmt_modified datetime nul ...

  2. Android适配器之ArrayAdapter、SimpleAdapter和BaseAdapter的简单用法与有用代码片段

    收藏ArrayAdapter.SimpleAdapter和BaseAdapter的一些简短代码片段,希望用时方便想起其用法. 1.ArrayAdapter 只可以简单的显示一行文本 代码片段: Arr ...

  3. 超实用的30 个简短的代码片段(一)

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:NicePython PS:如有需要Python学习资料的小伙伴可以加 ...

  4. 30秒内便能学会的30个超实用Python代码片段

    许多人在数据科学.机器学习.web开发.脚本编写和自动化等领域中都会使用Python,它是一种十分流行的语言. Python流行的部分原因在于简单易学. 本文将简要介绍30个简短的.且能在30秒内掌握 ...

  5. 第十一期:30秒内便能学会的30个实用Python代码片段

    许多人在数据科学.机器学习.web开发.脚本编写和自动化等领域中都会使用Python,它是一种十分流行的语言. Python流行的部分原因在于简单易学. 本文将简要介绍30个简短的.且能在30秒内掌握 ...

  6. 30个超实用Python代码片段

    版权声明:本文为博主'读芯术'原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 原文链接:https://blog.csdn.net/duxinshuxiaobia ...

  7. web简易计算器代码_30秒内便能学会的30个超实用Python代码片段

    许多人在数据科学.机器学习.web开发.脚本编写和自动化等领域中都会使用Python,它是一种十分流行的语言. Python流行的部分原因在于简单易学. 本文将简要介绍30个简短的.且能在30秒内掌握 ...

  8. java经典代码片段

    java经典代码片段 写这篇也是用于学习 package com.common.file; import java.io.File; import java.io.FileInputStream; i ...

  9. 20+个可重复使用的jQuery代码片段

    jQuery已经成为任何web项目的重要组成部分.它为网站提供了交互性的通过移动HTML元素,创建自定义动画,处理事件,选择DOM元素,检索整个document ,让最终用户有一个更好的体验. 在这篇 ...

最新文章

  1. 基于C++的骨架提取的鼻祖算法
  2. 学习新技能时,大脑在如何发生改变?
  3. 【No.3 Ionic】超级逗表情 App
  4. 【C 语言】文件操作 ( 配置文件读写 | 完整代码示例 ) ★
  5. 搭建glusterfs集群
  6. SpringBoot使用Thymeleaf模板
  7. oracle监听系统账号,linux 下 Oracle 监控sysdba用户登陆
  8. n条直线相交最多有几个邻补角_【初中数学】102条做初中几何辅导线的规律
  9. JQuery实现广告效果(滚动切换)
  10. python使用BytesIO或StringIO读写文件
  11. 专访 | 「Smartbi 」VP徐晶:未来,BI将成为决策者的诸葛亮
  12. 在JavaScript中解析查询字符串[重复]
  13. php语句过滤掉html标签_php过滤HTML标签、属性等正则表达式
  14. bfs--最小步数--青蛙
  15. 【待完善】MongoDB - 数据模型
  16. 理解ASP.NET MVC中的ActionResult
  17. ThingsBoard 提示Too many updates!
  18. Dwg,png,jpg,Dxf格式转换
  19. 数据可视化(全彩)(十二五国家重点图书出版规划项目)
  20. 如何实现excel的内容转成word报告模式

热门文章

  1. node+读写文件_python高级:2.文件读写part2
  2. Interpretation of 403 Bounded biharmonic weights
  3. 复杂背景下计算机视觉模型害虫识别的比较研究(像素语义分割网络SegNet)
  4. python怎么引入thrift文件_python使用thrift教程的方法示例
  5. lightGBM用于排序(Learning to Rank )
  6. laravel本地项目上传服务器,laravel 上传本地文件到服务器
  7. 查看电脑的CPU个数,核数,逻辑核数
  8. 是用于mac的linux,用于Mac OS X的Can(CUPS)ppd文件可用于Linux吗?
  9. mysql索引数据结构图解_MySQL索引底层结构与实现原理
  10. python和abap的关系_ABAP 一对多关系