(原创出处为本博客: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(chanint)无缓冲
c2:=make(chanint,1)有缓冲
例如:c1<-1
无缓冲:不仅仅是向c1通道放1,而是一直要等有别的携程<-c1接手了这个参数,那么c1<-1才会继续下去,要不然就一直阻塞着。
有缓冲:c2<-1则不会阻塞,因为缓冲大小是1(其实是缓冲大小为0),只有当放第二个值的时候,第一个还没被人拿走,这时候才会阻塞。
例子s
演示无缓存和有缓冲的channel的样子
1functest0(){ 2/**演示无缓存和有缓冲的channel的样子*/ 3done:=make(chanbool)/**无缓冲*/ 4done1:=make(chanbool,1)/**有缓冲*/ 5println(done,done1) 6}演示无缓冲在同一个main里面的死锁例子
1functest1(){ 2/**编译错误deadlock,阻死main进程*/ 3/**演示无缓冲在同一个main里面的死锁例子*/ 4done:=make(chanbool) 5done<-true/**这句是输入值,它会一直阻塞,等待读取*/ 6<-done/**这句是读取,但是在上面已经阻死了,永远走不到这里*/ 7println("完成") 8} 演示仅有输入语句,但没读取语句的死锁例子 1functest2(){ 2/**编译错误deadlock,阻死main进程*/ 3/**演示仅有输入语句,但没读取语句的死锁例子*/ 4done:=make(chanbool) 5done<-true/**输入,一直等待读取,哪怕没读取语句*/ 6println("完成") 7} 演示仅有读取语句,但没输入语句的死锁例子 1functest3(){ 2/**编译错误deadlock,阻死main进程*/ 3/**演示仅有读取语句,但没输入语句的死锁例子*/ 4done:=make(chanbool) 5<-done/**读取输出,前面没有输入语句,done是empty的,所以一直等待输入*/ 6 7println("完成") 8} 演示,协程的阻死,不会影响main 1functest4(){ 2/**编译通过*/ 3/**演示,协程的阻死,不会影响main*/ 4done:=make(chanbool) 5gofunc(){ 6<-done/**一直等待*/ 7}() 8println("完成") 9/** 10*控制台输出: 11*完成 12*/ 13} 在test4的基础上,无缓冲channel在协程goroutine里面阻塞死 1functest5(){ 2/**编译通过*/ 3/**在test4的基础上,无缓冲channel在协程goroutine里面阻塞死*/ 4done:=make(chanbool) 5gofunc(){ 6println("我可能会输出哦")/**阻塞前的语句*/ 7done<-true/**这里阻塞死,但是上面那句有可能输出,见test3的结论*/ 8println("我永远不会输出") 9<-done/**这句也不会走到,除非在别的协程里面读取,或者在main*/ 10}() 11println("完成") 12} 编译通过,在test5的基础上演示,延时main的跑完 1functest6(){ 2/**编译通过,在test5的基础上演示,延时main的跑完*/ 3done:=make(chanbool) 4gofunc(){ 5println("我可能会输出哦") 6done<-true/**这里阻塞死*/ 7println("我永远不会输出") 8<-done/**这句也不会走到*/ 9}() 10time.Sleep(time.Second*1)/**加入延时1秒*/ 11println("完成") 12/** 13*控制台输出: 14*我可能会输出哦 15*完成 16*/ 17/** 18*结论: 19*如果在goroutine中阻塞死,也可能不会把阻塞语句前的内容输出, 20*因为main已经跑完了,所以延时一会,等待goroutine 21*/ 22} 演示无缓冲channel在不同的位置里面接收填充和接收 1functest7(){ 2/**编译通过,演示无缓冲channel在不同的位置里面接收填充和接收*/ 3done:=make(chanbool) 4gofunc(){ 5done<-true/**直到,<-done执行,否则这里阻塞死*/ 6println("我永远不会输出,除非<-done执行") 7 8}() 9<-done/**这里接收,在输出完成之前,那么上面的语句将会走通*/ 10println("完成") 11/** 12*控制台输出: 13*我永远不会输出,除非<-done执行 14*完成 15*/ 16} 演示无缓冲channel在不同地方接收的影响 1functest8(){ 2/**编译通过,演示无缓冲channel在不同地方接收的影响*/ 3done:=make(chanbool) 4gofunc(){ 5done<-true/**直到,<-done执行,否则这里阻塞死*/ 6println("我永远不会输出,除非<-done执行") 7}() 8println("完成") 9<-done/**这里接收,在输出完成之后*/ 10/** 11*控制台输出: 12*完成 13*我永远不会输出,除非<-done执行 14*/ 15} 没缓存的channel使用close后,不会阻塞 1functest9(){ 2/**编译通过*/ 3/**演示,没缓存的channel使用close后,不会阻塞*/ 4done:=make(chanbool) 5close(done) 6//done<-true/**关闭了的,不能再往里面输入值*/ 7<-done/**这句是读取,但是在上面已经关闭channel了,不会阻死*/ 8println("完成") 9} 没缓存的channel,在goroutine里面使用close后,不会阻塞 1functest10(){ 2/**编译通过*/ 3/**演示,没缓存的channel,在goroutine里面使用close后,不会阻塞*/ 4done:=make(chanbool) 5gofunc(){ 6close(done) 7}() 8//done<-true/**关闭了的,不能再往里面输入值*/ 9<-done/**这句是读取,但是在上面已经关闭channel了,不会阻死*/ 10println("完成") 11} 有缓冲的channel不会阻塞的例子 1functest11(){ 2/**编译通过*/ 3/**有缓冲的channel不会阻塞的例子*/ 4done:=make(chanbool,1) 5done<-true 6<-done 7println("完成") 8} 有缓冲的channel会阻塞的例子 1functest12(){ 2/**编译通过*/ 3/**有缓冲的channel会阻塞的例子*/ 4done:=make(chanbool,1) 5//done<-true/**注释这句*/ 6<-done/**虽然是有缓冲的,但是在没输入的情况下,读取,会阻塞*/ 7println("完成") 8} 有缓冲的channel会阻塞的例子 1functest13(){ 2/**编译不通过*/ 3/**有缓冲的channel会阻塞的例子*/ 4done:=make(chanbool,1) 5done<-true 6done<-false/**放第二个值的时候,第一个还没被人拿走,这时候才会阻塞,根据缓冲值而定*/ 7println("完成") 8} 有缓冲的channel不会阻塞的例子 1functest14(){ 2/**编译通过*/ 3/**有缓冲的channel不会阻塞的例子*/ 4done:=make(chanbool,1) 5done<-true/**不会阻塞在这里,等待读取*/ 6 7println("完成") 8} 有缓冲的channel,如果在goroutine中使用,一定要做适当的延时,否则会输出来不及,因为main已经跑完了,所以延时一会,等待goroutine 1functest15(){ 2/**编译通过*/ 3/**有缓冲的channel在goroutine里面的例子*/ 4done:=make(chanbool,1) 5gofunc(){ 6/**不会阻塞*/ 7println("我可能会输出哦") 8done<-true/**如果把这个注释,也会导致<-done阻塞*/ 9println("我也可能会输出哦") 10<-done 11println("别注释done<-true哦,不然我就输出不了了") 12}() 13time.Sleep(time.Second*1)/**1秒延时,去掉就可能上面的都不会输出也有可以输出,routine调度*/ 14println("完成") 15/** 16*控制台输出: 17*我可能会输出哦 18*我也可能会输出哦 19*完成 20*/ 21/** 22*结论: 23*有缓冲的channel,如果在goroutine中使用,一定要做适当的延时,否则会输出来不及, 24*因为main已经跑完了,所以延时一会,等待goroutine 25*/ 26} 多channel模式 1funcgetMessagesChannel(msgstring,delaytime.Duration)<-chanstring{ 2c:=make(chanstring) 3gofunc(){ 4fori:=1;i<=3;i++{ 5c<-fmt.Sprintf("%s%d",msg,i) 6time.Sleep(time.Millisecond*delay)/**仅仅起到,下一次的c在何时输入*/ 7} 8}() 9returnc 10} 11 12functest16(){ 13/**编译通过*/ 14/**复杂的演示例子*/ 15/**多channel模式*/ 16c1:=getMessagesChannel("第一",600) 17c2:=getMessagesChannel("第二",500) 18c3:=getMessagesChannel("第三",5000) 19 20/**层层限制阻塞*/ 21/**这个for里面会造成等待输入,c1会阻塞c2,c2阻塞c3*/ 22/**所以它总是,先输出c1然后是c2最后是c3*/ 23fori:=1;i<=3;i++{ 24/**每次循环提取一轮,共三轮*/ 25println(<-c1)/**除非c1有输入值,否则就阻塞下面的c2,c3*/ 26println(<-c2)/**除非c2有输入值,否则就阻塞下面的c3*/ 27println(<-c3)/**除非c3有输入值,否则就阻塞进入下一轮循环,反复如此*/ 28} 29/** 30*这个程序的运行结果,首轮的,第一,第二,第三很快输出,因为 31*getMessagesChannel函数的延时在输入值之后,在第二轮及其之后 32*因为下一个c3要等到5秒后才能输入,所以会阻塞第二轮循环的开始5秒,如此反复。 33*/ 34/**修改:如果把getMessagesChannel里面的延时,放在输入值之前,那么c3总是等待5秒后输出*/ 35} 在test15基础修改的,复杂演示例,多channel的选择,延时在输入之后的情况 1functest17(){ 2/**编译通过*/ 3/**在test15基础修改的,复杂演示例子*/ 4/**多channel的选择,延时在输入之后的情况*/ 5c1:=getMessagesChannel("第一",600) 6c2:=getMessagesChannel("第二",500) 7c3:=getMessagesChannel("第三",5000) 8/**3x3次循环,是9*/ 9/**select总是会把最先完成输入的channel输出,而且,互不限制*/ 10/**c1,c2,c3每两个互不限制*/ 11fori:=1;i<=9;i++{ 12select{ 13casemsg:=<-c1: 14println(msg) 15casemsg:=<-c2: 16println(msg) 17casemsg:=<-c3: 18println(msg) 19} 20} 21/** 22*这个程序的运行结果: 23*第二1,第三1,第一1,第二2,第一2,第二3,第一3,第三2,第三3 24*/ 25/**分析:前3次输出,“第一”,“第二”,“第三”,都有,而且 26*是随机顺序输出,因为协程的调度,第4,5,6次,由于“第二”只延时500ms, 27*比600ms和5000ms都要小,那么它先输出,然后是“第一”,此时“第三”还不能输出, 28*因为它还在等5秒。此时已经输出5次,再过500ms,"第三"的5秒还没走完,所以继续输出"第一", 29*再过100ms,500+100=600,"第二"也再完成了一次,那么输出。至此,"第一"和"第二"已经 30*把管道的3个值全部输出,9-7=2,剩下两个是"第三"。此时,距离首次的5000ms完成, 31*还有,500-600-600=3800ms,达到后,"第三"将输出,再过5秒,最后一次"第三输出" 32*/ 33}欢迎转载
本文内容总结:
原文链接:https://www.cnblogs.com/linguanh/p/6248301.html