c := make(chan bool) //创建一个无缓冲的bool型Channel c <- x //向一个Channel发送一个值 <- c //从一个Channel中接收一个值 x = <- c //从Channel c接收一个值并将其存储到x中 x, ok = <- c //从Channel接收一个值,如果channel关闭了或没有数据,那么ok将被置为false ch chan int //可读写 ch1 chan<- int //ch1只能写 ch2 <-chan int //ch2只能读 channel是类型相关的,也就是一个channel只能传递一种类型
goroutine是Go语言中的轻量级线程实现,由Go运行时(runtime)管理.
先看一个例子:
func Sub(i int) {
fmt.Println("from sub func", i)
}
func main() {
for i := 0; i < 5; i++ {
Sub(i)
}
fmt.Println("from main")
}
这个例子做了一件事情,在main函数中串行执行了5次Sub函数.
如果我们需要Sub函数能够并发的执行,我们加个go,将每一个Sub函数放在goroutine中去(main函数其实也是个goroutine),代码如下所示:
func Sub(i int) {
fmt.Println("from sub func", i)
}
func main() {
for i := 0; i < 5; i++ {
go Sub(i)
}
fmt.Println("from main")
}
编译执行,你会发现只打印出了from main,Sub函数中字符并没有打印出来.这是因为主函数main启动了5个Sub函数后,并没有等待它们完成即退出了!这显然不是我们要的结果,我们使用go提供的消息通信机制channel来重构代码,保证Sub函数执行完成后,主函数再退出!
channel(信道)是什么,简单说,是goroutine之间互相通讯的东西。类似我们Unix上的管道(可以在进程间传递消息), 用来goroutine之间发消息和接收消息。其实,就是在做goroutine之间的内存共享。
func Sub(ch chan int) {
for i := 0; i < 5; i++ {
fmt.Println("from sub func", i)
}
ch <- 1 // 向channel存消息,如果没有其他goroutine取走数据,那么挂起Sub
}
func main() {
ch := make(chan int) // 创建了一个无缓存的channel
go Sub(ch) // 开启一个goroutine来执行Sub
fmt.Println("from main")
<-ch // 从channel取消息,如果没有写入消息,挂起main
}
无缓冲的信道在取消息和存消息的时候都会挂起当前的goroutine.
所以上面代码的执行流程是:
创建ch信道
新开goroutine来执行Sub,但因为ch信道中写入的消息没有被取走,Sub挂起.
from main被打印出来.
<-ch 挂起了main,直到取到数据,这就保证了Sub完成后,main才结束.
所以无缓存信道,存入数据必须取走,只存不取或取空的数据,都将导致死锁.
以下情形都是死锁:
//取不存在的消息: ch := make(chan int) <-ch //只写不取: ch := make(chan int) ch <- 1 //多个channel,ch1等待ch2的消息,但ch2没有消息写入: ch1 := make(chan int) ch2 := make(chan int) ch1 <- <-ch2 <-ch1
无缓存信道只负责流通消息,任何对该信道的读和写,都阻塞信道.
带缓存的channel,不仅可以流通数据,还可以缓存数据,只有达到缓存最大数目后,也就是缓存满了后,才阻塞信道,这听起来很像队列(Queue).其实,缓冲信道是先进先出的,我们可以把缓冲信道看作为一个线程安全的队列
ch := make(chan int, 2) // 带缓存channel,缓存数目2个,放入2个数据,不会挂起,只有放入第3个的时候才挂起当前goroutine
示例代码:
func main() {
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
}
对ch的写入,因为没有超过缓存数目3,所以不会阻塞;对ch取数据,将按先进先出,依次输出1 2 3
你会发现,带缓存的信道,取数据还是挺麻烦的.我们用for range来取数据
func main() {
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 4
for v := range ch {
fmt.Println(v)
}
}
报出deadlock,原因是ch没有关闭的情形下,range一直在读取.加一个判断:
func main() {
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 4
for v := range ch {
fmt.Println(v)
if len(ch) <= 0 {
break
}
}
}
我们判断了ch中有没有数据,没有数据就跳出循环.可以正常输出.当然我们也可以显式关闭信道.
func main() {
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 4
close(ch) // 关闭信道
for v := range ch {
fmt.Println(v)
}
}
1.无缓存
const MAX = 1000
var ch chan int
func Sub(i int) {
fmt.Println(i)
ch <- 1
}
func main() {
ch = make(chan int)
for i := 0; i < MAX; i++ {
go Sub(i)
}
for i := 0; i < MAX; i++ {
<-ch
}
}
2.带缓存
const MAX = 1000
var ch chan int
func Sub(i int) {
fmt.Println(i)
ch <- 1
}
func main() {
ch = make(chan int, MAX)
for i := 0; i < MAX; i++ {
go Sub(i)
}
for i := 0; i < MAX; i++ {
<-ch
}
}
两者效果相同,不同点在于:
无缓冲的信道是一批数据一个一个的流进流出
缓冲信道则是一个一个存储,然后一起流出去
顾名思义,单向channel只能用于发送或者接收数据。channel本身必然是同时支持读写的,否则根本没法用。假如一个channel真的只能读,那么肯定只会是空的,因为你没机会往里面写数据。同理,如果一个channel只允许写,即使写进去了,也没有丝毫意义,因为没有机会读取里面的数据。所谓的单向channel概念,其实只是对channel的一种使用限制。
ch1 chan<- int //ch1只能写
ch2 <-chan int //ch2只能读
示例:
func Parse(ch <-chan int) {
for value := range ch {
fmt.Println("Parsing value", value)
}
}
原文:http://www.cnblogs.com/itfenqing/p/7634211.html