管道(Channel)是Go语言中比较重要的部分,常常在Go中的并发中使用。今天尝试对Go语言的管道来作如下总结。总结的形式采用问答式的方法,让答案更有目的性。
Q1.管道是什么?
管道是Go语言在语言级别上提供的goroutine间的**通信方式**,咱们能够使用channel在多个goroutine之间传递消息。channel是**进程内**的通信方式,是不支持跨进程通讯的,若是须要进程间通信的话,能够使用Socket等网络方式。
以上是管道的概念,下面咱们就看下管道的语法。
Q2.管道的语法?
整个Go语言的语法都比较简洁,管道也不例外,其语法以下所示:
在此应当注意,管道是类型相关的,即一个管道只能传递一种类型的值。管道中的数据是先进先出的。网络
1 // 声明方式,在此ElemType是指此管道所传递的类型 2 var chanName chan ElemType 3 // 声明一个传递类型为int的管道 4 var ch chan int 5 // 声明一个map,元素是bool型的channel 6 var m map[string] chan bool 7 8 // 定义语法,定义须要使用内置函数make()便可,下面这行代码是声明+定义一个整型管道 9 ch := make(chan int) 10 // 事先定义好管道的size,下面这行代码定义管道的size为100 11 ch := make(chan int, 100) 12 13 // 由管道中读写数据,<-操做符是与最左边的chan优先结合的 14 // 向管道中写入一个数据,在此须要注意:向管道中写入数据一般会致使程序阻塞,直到有 15 // 其余goroutine从这个管道中读取数据 16 ch<- value 17 // 读取数据,注意:若是管道中没有数据,那么从管道中读取数据会致使程序阻塞,直到有数据 18 value := <-ch 19 20 // 单向管道 21 var ch1 chan<- float64 // 只能向里面写入float64的数据,不能读取 22 var ch2 <-chan int // 只能读取int型数据 23 24 // 关闭channel,直接调用close()便可 25 close(ch) 26 // 判断ch是否关闭,判断ok的值,若是是false,则说明已经关闭(关闭的话读取是不会阻塞的) 27 x, ok := <-ch
Q3.管道的使用场景?
在第一个问题中,咱们已经知道管道能够作进程间通信,Go中自带了对协程的支持(关键字go),而管道就是各个协程间通信的一个方法。这里咱们举些简单的小例子来讲明一下管道如何在协程中使用。多线程
1 package main 2 import "fmt" 3 4 func print() { 5 fmt.Println("Hello world") 6 } 7 8 func main() { 9 for i := 0; i < 10; i++ { 10 go print() 11 } 12 }
上面的代码意思大体是:使用协程来并行输出10次 "Hello world", 可是你们运行上面代码的时候,会发现不会有输出。这是由于虽然使用go关键字进行了协程的建立,可是尚未等到执行的时候,main函数已经退出来了,进程已经关闭,因此起来的协程也不会被执行。
若是你有C相关的多线程经验时,可已经将协程改成线程,以后调用线程的join方法,让主线程等待子线程执行完毕后再退出。而在Go语言中,咱们能够利用管道的写入阻塞和读取阻塞来完成相似线程join的行为。代码以下所示:
并发
1 package main 2 import "fmt" 3 4 func print(ch chan int) { 5 fmt.Println("Hello world") 6 ch<- 1 7 } 8 9 func main() { 10 chs := make([]chan int) 11 for i := 0; i < 10; i++ { 12 chs[i] = make(chan int) 13 go print(chs[i]) 14 } 15 16 for _, ch := range(chs){ 17 <-ch 18 } 19 }
经过以上代码,咱们就能够完成了并行输出10此Hello world 的效果。
有一个问题留给你们,若是将 print改成函数
1 func print(ch chan int){ 2 ch<- 1 3 fmt.Println("Hello world") 4 }
会打印出什么呢?
因为水平有限,不免会有错误,请你们指正。
谢谢。
[3/30]
spa