若是说goroutine是Go语言程序的并发体的话,那么channels则是它们之间的通讯机制。express
一个channel是一个通讯机制,它可让一个goroutine经过它给另外一个goroutine发送值信息。编程
每一个channel都有一个特殊的类型,也就是channels可发送数据的类型。一个能够发送int类型数据的channel通常写为chan int。缓存
使用内置的make函数,咱们能够建立一个channel:网络
ch := make(chan int) // ch has type 'chan int'
和map相似,channel也对应一个make建立的底层数据结构的引用。数据结构
当咱们复制一个channel或用于函数参数传递时,咱们只是拷贝了一个channel引用,所以调用者和被调用者将引用同一个channel对象。并发
和其它的引用类型同样,channel的零值也是nil。app
两个相同类型的channel可使用==运算符比较。若是两个channel引用的是相同的对象,那么比较的结果为真。一个channel也能够和nil进行比较。tcp
一个channel有发送和接受两个主要操做,都是通讯行为。函数
一个发送语句将一个值从一个goroutine经过channel发送到另外一个执行接收操做的goroutine。线程
发送和接收两个操做都使用<-
运算符。
在发送语句中,<-
运算符分割channel和要发送的值。
在接收语句中,<-
运算符写在channel对象以前。一个不使用接收结果的接收操做也是合法的。
ch <- x // a send statement x = <-ch // a receive expression in an assignment statement <-ch // a receive statement; result is discarded
Channel还支持close操做,用于关闭channel,随后对基于该channel的任何发送操做都将致使panic异常。
对一个已经被close过的channel进行接收操做依然能够接受到以前已经成功发送的数据;若是channel中已经没有数据的话将产生一个零值的数据。
使用内置的close函数就能够关闭一个channel:
close(ch)
以最简单方式调用make函数建立的是一个无缓存的channel。
可是咱们也能够指定第二个整型参数,对应channel的容量。若是channel的容量大于零,那么该channel就是带缓存的channel。
ch = make(chan int) // unbuffered channel ch = make(chan int, 0) // unbuffered channel ch = make(chan int, 3) // buffered channel with capacity 3
一个基于无缓存Channels的发送操做将致使发送者goroutine阻塞,直到另外一个goroutine在相同的Channels上执行接收操做,当发送的值经过Channels成功传输以后,两个goroutine能够继续执行后面的语句。反之,若是接收操做先发生,那么接收者goroutine也将阻塞,直到有另外一个goroutine在相同的Channels上执行发送操做。
基于无缓存Channels的发送和接收操做将致使两个goroutine作一次同步操做。由于这个缘由,无缓存Channels有时候也被称为同步Channels。当经过一个无缓存Channels发送数据时,接收者收到数据发生在唤醒发送者goroutine以前(译注:happens before,这是Go语言并发内存模型的一个关键术语!)。
在讨论并发编程时,当咱们说x事件在y事件以前发生(happens before),咱们并非说x事件在时间上比y时间更早;咱们要表达的意思是要保证在此以前的事件都已经完成了,例如在此以前的更新某些变量的操做已经完成,你能够放心依赖这些已完成的事件了。
当咱们说x事件既不是在y事件以前发生也不是在y事件以后发生,咱们就说x事件和y事件是并发的。这并非意味着x事件和y事件就必定是同时发生的,咱们只是不能肯定这两个事件发生的前后顺序。在下一章中咱们将看到,当两个goroutine并发访问了相同的变量时,咱们有必要保证某些事件的执行顺序,以免出现某些并发问题。(意思就是不肯定线程执行顺序)
func main() { conn, err := net.Dial("tcp", "localhost:8000") if err != nil { log.Fatal(err) } done := make(chan struct{}) go func() { io.Copy(os.Stdout, conn) // NOTE: ignoring errors log.Println("done") done <- struct{}{} // signal the main goroutine }() mustCopy(conn, os.Stdin) conn.Close() <-done // wait for background goroutine to finish }
当用户关闭了标准输入,主goroutine中的mustCopy函数调用将返回,而后调用conn.Close()关闭读和写方向的网络链接。
关闭网络链接中的写方向的链接将致使server程序收到一个文件(end-of-file)结束的信号。
关闭网络链接中读方向的链接将致使后台goroutine的io.Copy函数调用返回一个“read from closed connection”(“从关闭的链接读”)相似的错误,所以咱们临时移除了错误日志语句;(须要注意的是go语句调用了一个函数字面量,这Go语言中启动goroutine经常使用的形式。)
在后台goroutine返回以前,它先打印一个日志信息,而后向done对应的channel发送一个值。主goroutine在退出前先等待从done对应的channel接收一个值。所以,老是能够在程序退出前正确输出“done”消息。
基于channels发送消息有两个重要方面。首先每一个消息都有一个值,可是有时候通信的事实和发生的时刻也一样重要。当咱们更但愿强调通信发生的时刻时,咱们将它称为消息事件。有些消息事件并不携带额外的信息,它仅仅是用做两个goroutine之间的同步,这时候咱们能够用struct{}
空结构体做为channels元素的类型,虽然也可使用bool或int类型实现一样的功能,done <- 1
语句也比done <- struct{}{}
更短。