GoLang基础——管道&反射
管道
管道用于goroutine之间相互通信,每个管道都有属于自己的数据类型。
直接用例子说明:
package main import "fmt" func main(){ //声明一个管道 var ch chan bool //开启该管道 ch = make(chan bool) go func(){ for i:=0;i<10;i++{ fmt.Println("子goroutine中,",i) } //子协程中完成操作后,将一个true信号写入管道 ch<-true fmt.Println("子goroutine结束") }() //主协程等待读入管道中的true,读到后阻塞解除,继续运行 data:=<-ch fmt.Println(data) fmt.Println("main goroutine结束") }
例子中包含了管道的声明,开启,以及读写操作。
主协程中的读操作是阻塞式的。也就是说,如果子线程没有写入数据,它将一直等待。
读多个数据时,可用range循环来读取,写数据的一方在写完所有数据后要关闭管道,否则读数据一方会一直等待。
代码演示如下:
package main import ( "fmt" "time" ) func main(){ //声明+启动管道ch ch:=make(chan int) //调用子协程 go sendData(ch) //用range来读取管道ch中的数据 for v := range ch{ fmt.Println("读取数据:",v) } fmt.Println("main..over") } func sendData(ch chan int){ for i:=0;i<10;i++{ //每次写完一个数据后都睡眠1s time.Sleep(1*time.Second) //向管道中写数据 ch<-i } //写完所有数据后,关闭管道,否则读数据的一方会一直等待 close(ch) }
缓冲通道:缓冲通道带有一个指定容量的缓冲区。写数据时,只有缓冲区满了才会发生阻塞。读数据时,缓冲区空了才会发生阻塞。代码示例:
package main import ( "fmt" "strconv" ) func main(){ //声明+启动管道ch ch:=make(chan string,4) //调用子协程 go sendData(ch) //读取管道ch中的数据 for { //读取时,ok为true;读完后,ok为false v,ok := <-ch if !ok{ fmt.Println("读完了",ok) break } fmt.Println("读取的数据是:",v) } fmt.Println("main..over") } func sendData(ch chan string){ for i:=0;i<10;i++{ //向管道中写数据 ch<-"数据"+strconv.Itoa(i) fmt.Println("子协程中写出第%d个数据",i) } //写完所有数据后,关闭管道 close(ch) }
定向通道:只读/只写
//只读管道 ch1:=make(<- chan int) //只写管道 ch2:=make(chan <- int)
select语句和switch语句很相似,也有case和default,它的每个分支都是一个通信,运行时会随机执行某一个可运行的case。若无可运行case,则运行default分支。代码示例:
package main import ( "fmt" "time" ) func main(){ ch1:=make(chan int) ch2:=make(chan int) go func(){ //3秒后往ch1中写入数据 time.Sleep(3*time.Second) ch1<-100 }() select{ //3秒后ch1是可执行的 case<-ch1: fmt.Println("case1可执行") //ch2中无数据,不可执行 case<-ch2: fmt.Println("case2可执行") //3秒后ch3是可执行的 case<-time.After(3*time.Second): fmt.Println("case3可执行") default: fmt.Println("执行了default") } }
反射:
在运行时更新变量和检查它们的值,调用它们的方法,但是在编译时并不知道这些变量的具体类型。
反射最终目的是在运行时能够动态地获取一个接口变量的值和类型。
需要反射的场景:
编写函数时,不知道传入的参数类型是什么。
根据某些条件决定调用哪个函数,需要对函数和函数的参数进行反射,在运行期间动态执行函数。
反射的步骤:
有一个接口类型的变量。
把它转成reflect对象。(type或value)
根据不同情况调用不同的函数。
func main(){ var x float64 = 3.4 //将反射的值存储于变量v中 v:=reflect.ValueOf(x) //可以调用函数获取v的值与类型 fmt.Println("type:",v.Type()) fmt.Println("value:",v.Float()) t:=reflect.TypeOf(x) fmt.Println("type:",t) }
静态类型:编译时就知道变量类型
动态类型:运行时才知变量类型(空接口)