目录
- interrupt := make(chan os.Signal) // 可以控制强制终止的信号
- // 如果系统有中断信号,发送给r.interrupt
- signal.Notify(interrupt, os.Interrupt)
-
-
- // Ctrl+C 退出
- sig := make(chan os.Signal, 1)
- signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
- fmt.Printf("quit (%v)\n", <-sig)
通道接收的多参返回,如果可以接收的话,第一参数是接收的值,第二个表示通道是否关闭,false表示通道关闭,true表示通道正常。
- res := make(chan io.Closer, size)
- r, ok := <- res
- func main() {
- ctx, cancel := context.WithCancel(context.Background())
- go func(ctx context.Context) {
- for {
- select {
- case <-ctx.Done():
- fmt.Println("监控退出,停止了...")
- return
- default:
- fmt.Println("goroutine监控中...")
- time.Sleep(2 * time.Second)
- }
- }
- }(ctx)
-
- time.Sleep(10 * time.Second)
- fmt.Println("可以了,通知监控停止")
- cancel()
- //为了检测监控过是否停止,如果没有监控输出,就表示停止了
- time.Sleep(5 * time.Second)
-
- }
context.Background() 返回一个空的Context,这个空的Context一般用于整个Context树的根节点。然后我们使用context.WithCancel(parent)函数,创建一个可取消的子Context,然后当作参数传给goroutine使用,这样就可以使用这个子Context跟踪这个goroutine。在goroutine中,使用select调用<-ctx.Done()判断是否要结束,如果接受到值的话,就可以返回结束goroutine了;如果接收不到,就会继续进行监控。
那么是如何发送结束指令的呢?这就是示例中的cancel函数啦,它是我们调用context.WithCancel(parent)函数生成子Context的时候返回的,第二个返回值就是这个取消函数,它是CancelFunc类型的。我们调用它就可以发出取消指令,然后我们的监控goroutine就会收到信号,就会返回结束。
使用Context控制一个goroutine的例子如上,非常简单,下面我们看看控制多个goroutine的例子,其实也比较简单。
- func main() {
- ctx, cancel := context.WithCancel(context.Background())
- go watch(ctx,"【监控1】")
- go watch(ctx,"【监控2】")
- go watch(ctx,"【监控3】")
-
- time.Sleep(10 * time.Second)
- fmt.Println("可以了,通知监控停止")
- cancel()
- //为了检测监控过是否停止,如果没有监控输出,就表示停止了
- time.Sleep(5 * time.Second)
- }
-
- func watch(ctx context.Context, name string) {
- for {
- select {
- case <-ctx.Done():
- fmt.Println(name,"监控退出,停止了...")
- return
- default:
- fmt.Println(name,"goroutine监控中...")
- time.Sleep(2 * time.Second)
- }
- }
- }
- func main() {
- u:= User{"张三",20}
- t:=reflect.TypeOf(u)
- v := reflect.ValueOf(u)
- fmt.Println(t, v)
-
- // 一种简单的打印变量类型和变量值的方法
- fmt.Printf("%T, %v", u, u)
- }
-
- type User struct{
- Name string
- Age int
- }
通过反射,我们可以获取一个结构体类型的字段,也可以获取一个类型的导出方法,这样我们就可以在运行时了解一个类型的结构,这是一个非常强大的功能。
- for i:=0;i<t.NumField();i++ {
- fmt.Println(t.Field(i).Name)
- }
-
- for i:=0;i<t.NumMethod() ;i++ {
- fmt.Println(t.Method(i).Name)
- }
假如我们想在运行中动态的修改某个字段的值有什么办法呢?一种就是我们常规的有提供的方法或者导出的字段可以供我们修改,还有一种是使用反射,这里主要介绍反射。
- func main() {
- x:=2
- v:=reflect.ValueOf(&x)
- v.Elem().SetInt(100)
- fmt.Println(x)
- }
因为reflect.ValueOf函数返回的是一份值的拷贝,所以前提是我们是传入要修改变量的地址。 其次需要我们调用Elem方法找到这个指针指向的值。 最后我们就可以使用SetInt方法修改值了。
以上有几个重点,才可以保证值可以被修改,Value为我们提供了CanSet方法可以帮助我们判断是否可以修改该对象。
结构体的方法我们不光可以正常的调用,还可以使用反射进行调用。要想反射调用,我们先要获取到需要调用的方法,然后进行传参调用,如下示例:
- func main() {
- u:=User{"张三",20}
- v:=reflect.ValueOf(u)
-
- mPrint:=v.MethodByName("Print")
- args:=[]reflect.Value{reflect.ValueOf("前缀")}
- fmt.Println(mPrint.Call(args))
-
- }
-
- type User struct{
- Name string
- Age int
- }
-
- func (u User) Print(prfix string){
- fmt.Printf("%s:Name is %s,Age is %d",prfix,u.Name,u.Age)
- }
MethodByName方法可以让我们根据一个方法名获取一个方法对象,然后我们构建好该方法需要的参数,最后调用Call就达到了动态调用方法的目的。
获取到的方法我们可以使用IsValid 来判断是否可用(存在)。
这里的参数是一个Value类型的数组,所以需要的参数,我们必须要通过ValueOf函数进行转换。
- func main() {
- var u User
- h:=`{"name":"张三","age":15}`
- err:=json.Unmarshal([]byte(h),&u)
- if err!=nil{
- fmt.Println(err)
- }else {
- fmt.Println(u)
- }
- }
-
- type User struct{
- Name string `name`
- Age int `age`
- }
可以通过反射获取字段的tag
- func main() {
- var u User
-
- t:=reflect.TypeOf(u)
-
- for i:=0;i
.NumField();i++{ - sf:=t.Field(i)
- fmt.Println(sf.Tag)
- }
- }
很多时候我们的一个Struct不止具有一个功能,比如我们需要JSON的互转、还需要BSON以及ORM解析的互转,所以一个字段可能对应多个不同的Tag,以便满足不同的功能场景。Go Struct 为我们提供了键值对的Tag,来满足我们以上的需求。
- func main() {
- var u User
- t:=reflect.TypeOf(u)
-
- for i:=0;i
.NumField();i++{ - sf:=t.Field(i)
- fmt.Println(sf.Tag.Get("json"))
- }
-
-
- }
-
- type User struct{
- Name string `json:"name"`
- Age int `json:"age"`
- }
也可以设置多个key
- func main() {
- var u User
- t:=reflect.TypeOf(u)
-
- for i:=0;i
.NumField();i++{ - sf:=t.Field(i)
- fmt.Println(sf.Tag.Get("json"),",",sf.Tag.Get("bson"))
- }
-
-
- }
-
- type User struct{
- Name string `json:"name" bson:"b_name"`
- Age int `json:"age" bson:"b_age"`
- }
多个Key使用空格进行分开,然后使用Get方法获取不同Key的值。
Struct Tag可以提供字符串到Struct的映射能力,以便我们作转换,除此之外,还可以作为字段的元数据的配置,提供我们需要的配置,比如生成Swagger文档等。