(原创出处为本博客:http://www.cnblogs.com/linguanh/)

前序:

  因为打算自己搞个基于Golang的IM服务器,所以复习了下之前一直没怎么使用的协程、管道等高并发编程知识。发现自己的channel这块,也就是管道,实在是有些混乱。然后对着文档,边参照官网例子和在编译器测试,总结了下面这17个例子,设置为简短的片段,是为了免得混淆太多,阻碍理解。内含注释丰富,复制粘贴就能编译使用。

  这里立个 flag,有错误欢迎指出,只要你跟着敲完这17个例子,channel的基础绝对可以掌握!

基本概念:

  关于管道 Channel:

    Channels用来同步并发执行的函数并提供它们某种传值交流的机制。

    Channels的一些特性:通过channel传递的元素类型、容器(或缓冲区)和传递的方向由“<-”操作符指定。

    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),只有当放第二个值的时候,第一个还没被人拿走,这时候才会阻塞。

例子s

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

 func test0(){
/** 演示 无缓存 和 有缓冲 的 channel 的样子 */
done := make(chan bool) /** 无缓冲 */
done1 := make(chan bool,) /** 有缓冲 */
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 秒 */
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 /** 关闭了的,不能再往里面输入值 */
<-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,)
done<-true
<-done
println("完成")
}
有缓冲的 channel 会阻塞的例子
 func test12()  {
/** 编译通过 */
/** 有缓冲的 channel 会阻塞的例子 */
done := make(chan bool,)
// done<-true /** 注释这句 */
<-done /** 虽然是有缓冲的,但是在没输入的情况下,读取,会阻塞 */
println("完成")
}
有缓冲的 channel 会阻塞的例子
 func test13()  {
/** 编译不通过 */
/** 有缓冲的 channel 会阻塞的例子 */
done := make(chan bool,)
done<-true
done<-false /** 放第二个值的时候,第一个还没被人拿走,这时候才会阻塞,根据缓冲值而定 */
println("完成")
}
有缓冲的 channel 不会阻塞的例子
 func test14()  {
/** 编译通过 */
/** 有缓冲的 channel 不会阻塞的例子 */
done := make(chan bool,)
done<-true /** 不会阻塞在这里,等待读取 */ println("完成")
}
有缓冲的channel,如果在 go routine 中使用,一定要做适当的延时,否则会输出来不及,因为main已经跑完了,所以延时一会,等待 go routine
 func test15()  {
/** 编译通过 */
/** 有缓冲的channel 在 go routine 里面的例子 */
done := make(chan bool,)
go func() {
/** 不会阻塞 */
println("我可能会输出哦")
done<-true /** 如果把这个注释,也会导致 <-done 阻塞 */
println("我也可能会输出哦")
<-done
println("别注释 done<-true 哦,不然我就输出不了了")
}()
time.Sleep(time.Second * ) /** 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 := ; i <= ; i++ {
c <- fmt.Sprintf("%s %d", msg, i)
time.Sleep(time.Millisecond * delay) /** 仅仅起到,下一次的 c 在何时输入 */
}
}()
return c
} func test16() {
/** 编译通过 */
/** 复杂的演示例子 */
/** 多channel模式 */
c1 := getMessagesChannel("第一", )
c2 := getMessagesChannel("第二", )
c3 := getMessagesChannel("第三", ) /** 层层限制阻塞 */
/** 这个 for 里面会造成等待输入,c1 会阻塞 c2 ,c2 阻塞 c3 */
/** 所以它总是,先输出 c1 然后是 c2 最后是 c3 */
for i := ; i <= ; i++ {
/** 每次循环提取一轮,共三轮 */
println(<-c1) /** 除非 c1 有输入值,否则就阻塞下面的 c2,c3 */
println(<-c2) /** 除非 c2 有输入值,否则就阻塞下面的 c3 */
println(<-c3) /** 除非 c3 有输入值,否则就阻塞进入下一轮循环,反复如此 */
}
/**
* 这个程序的运行结果,首轮的,第一,第二,第三 很快输出,因为
* getMessagesChannel 函数的延时 在 输入值之后,在第二轮及其之后
* 因为下一个 c3 要等到 5秒后才能输入,所以会阻塞第二轮循环的开始5秒,如此反复。
*/
/** 修改:如果把 getMessagesChannel 里面的延时,放在输入值之前,那么 c3 总是等待 5秒 后输出 */
}
在 test15 基础修改的,复杂演示例,多channel 的选择,延时在输入之后的情况
 func test17()  {
/** 编译通过 */
/** 在 test15 基础修改的,复杂演示例子 */
/** 多channel 的选择,延时在输入之后的情况 */
c1 := getMessagesChannel("第一", )
c2 := getMessagesChannel("第二", )
c3 := getMessagesChannel("第三", )
/** 3x3 次循环,是 9 */
/** select 总是会把最先完成输入的channel输出,而且,互不限制 */
/** c1,c2,c3 每两个互不限制 */
for i := ; i <= ; 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秒,最后一次"第三输出"
*/
}

欢迎转载

Golang, 以17个简短代码片段,切底弄懂 channel 基础的更多相关文章

  1. [Go] 通过 17 个简短代码片段,切底弄懂 channel 基础

    关于管道 Channel Channel 用来同步并发执行的函数并提供它们某种传值交流的机制. Channel 的一些特性:通过 channel 传递的元素类型.容器(或缓冲区)和 传递的方向由“&l ...

  2. Golang, 以 9 个简短代码片段,弄懂 defer 的使用特点

    作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...

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

    摘自:http://blog.csdn.net/shakespeare001/article/details/7926783 Adapter是连接后端数据和前端显示的适配器接口,是数据Data和UI( ...

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

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

  5. 微信小程序代码片段

    微信小程序代码片段是一种可分享的小项目,可用于分享小程序和小游戏的开发经验.展示组件和 API 的使用.复现开发问题等等.分享代码片段会得到一个链接,所有拥有此分享链接的人可以在工具中导入此代码片段. ...

  6. python超实用的30 个简短的代码片段(二)

    Python是目前最流行的语言之一,它在数据科学.机器学习.web开发.脚本编写.自动化方面被许多人广泛使用. 它的简单和易用性造就了它如此流行的原因. 如果你正在阅读本文,那么你或多或少已经使用过P ...

  7. golang代码片段(摘抄)

    以下是从golang并发编程实战2中摘抄过来的代码片段,主要是实现一个简单的tcp socket通讯(客户端发送一个数字,服务端计算该数字的立方根然后返回),写的不错,用到了go的并发以及看下郝林大神 ...

  8. python超实用的30 个简短的代码片段(三)

    Python是目前最流行的语言之一,它在数据科学.机器学习.web开发.脚本编写.自动化方面被许多人广泛使用. 它的简单和易用性造就了它如此流行的原因. 如果你正在阅读本文,那么你或多或少已经使用过P ...

  9. 30+有用的CSS代码片段

    在一篇文章中收集所有的CSS代码片段几乎是不可能的事情,但是我们这里列出了一些相对于其他的更有用的代码片段,不要被这些代码的长度所吓到,因为它们都很容易实现,并且具有良好的文档.除了那些解决常见的恼人 ...

随机推荐

  1. 微信小程序开发心得

    微信小程序也已出来有一段时间了,最近写了几款微信小程序项目,今天来说说感受. 首先开发一款微信小程序,最主要的就是针对于公司来运营的,因为,在申请appid(微信小程序ID号)时候,需要填写相关的公司 ...

  2. 旺财速啃H5框架之Bootstrap(四)

    上一篇<<旺财速啃H5框架之Bootstrap(三)>>已经把导航做了,接下来搭建内容框架.... 对于不规整的网页,要做成自适应就有点玩大了.... 例如下面这种版式的页面. ...

  3. MSDN文档篇

    很多人网上下载3~10G不等的MSDN文档,发现,下载完成了不会用 很多人每次都得在线下载文档,手上万千PC,都重新下载不是得疯了? so==> 先看几张图 推荐一个工具:https://vsh ...

  4. 网站定位之---根据IP获得区域

    记得以前做一个培训机构网站时候需要定位,那时候用的搜狐的api,不是很精准. demo:https://github.com/dunitian/LoTCodeBase/tree/master/NetC ...

  5. 引人瞩目的 CSS 变量(CSS Variable)

    这是一个令人激动的革新. CSS 变量,顾名思义,也就是由网页的作者或用户定义的实体,用来指定文档中的特定变量. 更准确的说法,应该称之为 CSS 自定义属性 ,不过下文为了好理解都称之为 CSS 变 ...

  6. 操作系统篇-分段机制与GDT|LDT

    || 版权声明:本文为博主原创文章,未经博主允许不得转载. 一.前言     在<操作系统篇-浅谈实模式与保护模式>中提到了两种模式,我们说在操作系统中,其实大部分时间是待在保护模式中的. ...

  7. php报错 ----> Call to undefined function imagecreatetruecolor()

    刚才在写验证码的时候,发现报错,然后排查分析了一下,原来是所用的php版本(PHP/5.3.13)没有开启此扩展功能. 进入php.ini 找到extension=php_gd2.dll ,将其前面的 ...

  8. 通过AngularJS实现前端与后台的数据对接(一)——预备工作篇

    最近,笔者在做一个项目:使用AngularJS,从而实现前端与后台的数据对接.笔者这是第一次做前端与后台的数据对接的工作,因此遇到了许多问题.笔者在这些问题中,总结了一些如何实现前端与后台的数据对接的 ...

  9. golang语言构造函数

    1.构造函数定义 构造函数 ,是一种特殊的方法.主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中.特别的一个类可以有多个构造函数 ,可根据其参数个 ...

  10. C#与C++通信

    # C#与C++相互发送消息 # ## C#端: ## namespace CshapMessage { /// /// MainWindow.xaml 的交互逻辑 /// public partia ...