GoLang核心知识点

目录

[1. 系统中断信号注册](#1. 系统中断信号注册)

[2. 通道接收多个返回值](#2. 通道接收多个返回值)

[3. go context](#3. go context)

[4. reflect](#4. reflect)

[5. json字符串对象转换](#5. json字符串对象转换)


1. 系统中断信号注册

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)

2. 通道接收多个返回值

通道接收的多参返回,如果可以接收的话,第一参数是接收的值,第二个表示通道是否关闭,false表示通道关闭,true表示通道正常。

res := make(chan io.Closer, size)
r, ok := <- res

3. go context

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)
		}
	}
}
  • 不要把Context放在结构体中,要以参数的方式传递
  • 以Context作为参数的函数方法,应该把Context作为第一个参数,放在第一位。
  • 给一个函数方法传递Context的时候,不要传递nil,如果不知道传递什么,就使用context.TODO
  • Context的Value相关方法应该传递必须的数据,不要什么数据都使用这个传递
  • Context是线程安全的,可以放心的在多个goroutine中传递

4. reflect

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函数进行转换。

5. json字符串对象转换

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<t.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<t.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<t.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文档等。

相关推荐
Ai 编码助手19 分钟前
MySQL中distinct与group by之间的性能进行比较
数据库·mysql
陈燚_重生之又为程序员34 分钟前
基于梧桐数据库的实时数据分析解决方案
数据库·数据挖掘·数据分析
caridle36 分钟前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
白云如幻38 分钟前
MySQL排序查询
数据库·mysql
萧鼎39 分钟前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
学地理的小胖砸40 分钟前
【一些关于Python的信息和帮助】
开发语言·python
疯一样的码农40 分钟前
Python 继承、多态、封装、抽象
开发语言·python
^velpro^41 分钟前
数据库连接池的创建
java·开发语言·数据库
荒川之神1 小时前
ORACLE _11G_R2_ASM 常用命令
数据库·oracle
秋の花1 小时前
【JAVA基础】Java集合基础
java·开发语言·windows