go的横空出世,让很多人眼前一亮,它的语法以简洁著称,并且它对多核并发的原生支持,让他在云计算和分布式领域展露头脚,它的核心围绕channel和goroutine展开。
首先golang channel 分为有缓冲与无缓冲两种类型,很多人认为无缓冲channel单单只是 默认缓冲为1缓冲的channel,其实它们最大的区别是阻塞问题。如下
c1:=make(chan int) 无缓冲
c2:=make(chan int,1) 有缓冲
c1<-1
在c1<- 1处,因为c1是无缓冲的,因此程序会阻塞在这里,不会往下执行,但是c2则不会,他在channel满之前是不会阻塞的,他会继续往下执行,但是如果你在c2第一个数据未取出之前继续塞第二个数据,就会阻塞在你放第二个数据处,因为她的缓冲区单位只有1.例:
dgucochann := make(chan int,2)
dgucochann <- 1
fmt.Println("Hello DGuco")
dgucochann <- 2
fmt.Println("Hello Yuki")
dgucochann <- 3
fmt.Println("Hello Go")
在这种情况下,程序在第6行处会阻塞,会打印前两句话Hello DGuco和Hello Yuki,因为该channel只有两个单位的缓冲,在第6行时没有另外一个goroutine去取出里面的数据,所以阻塞在这里。
下面我们来介绍一下channel的基本用法。
1 信号量的传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
来自CODE的代码片
dgucosignal.go
可以看到依次打出了相应的两段文字,如果把第6行去掉,因为在dgucofunc2收不到来自channel的信号就会阻塞,结果只会打印In dguco_func1
2 生产者消费者问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
来自CODE的代码片
dgucocustomer.go
这个很简单,执行结果依次打印0-9
3 定时器(这个经常用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
来自CODE的代码片
dgucotimer.go
顺便说一下select的作用,首先它就相当于switch,但是他是用于channel的,举个例子:
case <- c1:
.........
case <- c2:
........:
}
}
当程序执行到这里时,程序会阻塞在此,直到c1或者c2中有数据被放入就会继续执行,当然一般不会这样写,因为如果c1,和c2永远收不到数据程序阻塞在这里肯定不是我们想要的结果,一般我们都会写一个超时处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
来自CODE的代码片
dgucotimeout.go
4 goroutine 通信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
来自CODE的代码片
dgucodemo.go
这里实现了一个goroutine写,两个goroutine读的过程,可以看到按顺序写进去的数据,被哪个goroutine读到是完全随机的,在golang中我们要实现进程间通信,channel是唯一途径,也是推荐的途径,她的底层是通过共享内存实现的,速度非常快,这样我们摆脱了linux系统繁琐,古老的进程间通信方式,管道,消息队列,信号量等,非常的方便。
最后做一个总结,在使用channel的时候,一定要至少有一个goroutine来负责读(除非你有特殊的需求),否则你的程序就会阻塞在你写channel的地方。