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

相关推荐
破茧蝴蝶2 小时前
Python分布式爬虫完整教程
开发语言·python
数据库小学妹2 小时前
国产数据库怎么选?兼容、迁移、核心案例与融合架构四道关
数据库·架构·国产数据库·数据库迁移·融合数据库
狗凯之家源码网2 小时前
全新 PHP 轻量客服聊天室源码深度评测
开发语言·php·聊天室系统
远离UE43 小时前
UE5 Substrate Thin Film 源码拆析
开发语言·javascript·ue5
keyipatience3 小时前
JavaScript学习(1)书写位置,变量、数据类型、运算符
开发语言·前端·javascript·css·学习
IvorySQL3 小时前
PostgreSQL 日报| PgBouncer 修复三个 CVE 漏洞(10 月 15 日)
数据库·postgresql·区块链
饺子大魔王的男人3 小时前
后台表格、查询、流程总在重复写?我用ToolJet把PostgreSQL直接做成可远程访问的业务系统
数据库·postgresql
Gauss松鼠会3 小时前
高斯数据库(openGauss)的物理存储结构
运维·服务器·数据库·sql·opengauss·经验总结
栈帧之外3 小时前
【C++】STL之set
开发语言·c++