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)
}

静态类型:编译时就知道变量类型

动态类型:运行时才知变量类型(空接口)

posted @ 2020-07-08 15:34  菅兮徽音  阅读(217)  评论(0编辑  收藏  举报