cgo
在 Go 中调用 C 代码:
/*
int sum(int a, int b) {
return a+b;
}
*/
import "C"
func main() {
println(C.sum(1, 1))
}
- cgo 是让 go 语言调用 C 方法的技术
- cgo 需要 go 调度器和协程栈的配合
- cgo 一般用于找不到 go 实现的情况
- cgo 不能提高性能,是一个临时解决方案
defer
defer:函数退出前一定会执行 defer 后的语句。
思路:
- 协程记录 defer 信息,函数退出时调用
- 将 defer 代码直接编译进函数尾
1.1 堆上分配
- 1.12 之前使用
- 在堆上开辟一个
sched.deferpool
- 遇到 defer 语句,将信息放入
deferpool
- 函数返回时,从
deferpool
取出执行
deferpool
记录在线程结构体 p
上:
type p struct {
// ...
deferpool []*_defer // pool of available defer structs (see panic.go)
deferpoolbuf [32]*_defer
// ...
}
1.2 栈上分配
- 1.13 之后出现
- 遇到 defer 语句,将信息放到栈上
- 函数返回时,从栈中取出执行
- 只能保存一个 defer 信息
2.1 开放编码
- 1.14 之后出现
- 如果 defer 语句在编译时就可以确定,则直接改写用户代码,defer 语句放入函数末尾
func main() {
defer fmt.Println("defer1")
defer fmt.Println("defer2")
fmt.Println("main")
}
recover、panic
panic
- panic 会抛出错误
- 终止协程运行
- 带崩整个 Go 程序
func main() {
go func() {
panic("panic")
fmt.Println("do")
}()
fmt.Println("main")
time.Sleep(1 * time.Second)
/*
main
panic: panic
*/
}
panic + defer
- panic 在退出协程之前会执行本协程所有已注册的 defer
- 不会执行其他协程的 defer
func main() {
defer fmt.Println("defer1")
go func() {
defer fmt.Println("defer2")
panic("panic1")
fmt.Println("do")
}()
fmt.Println("main")
time.Sleep(1 * time.Second)
/**
main
defer2
panic: panic1
*/
}
panic + defer + recover
在 defer 中执行 recover 可以拯救协程
- 如果涉及到 recover,defer 会使用堆上分配
- 遇到 panic,panic 会从 deferpool 中取出 defer 语句并执行
- defer 中调用 recover,可以终止 panic 的过程
func main() {
defer fmt.Println("defer1")
go func(){
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered. Error: ", r)
}
}()
panic("panic1")
fmt.Println("do")
}()
fmt.Println("main")
time.Sleep(1 * time.Second)
/**
main
Recovered. Error: panic1
defer1
*/
}
反射 reflect
func main() {
s := "moody"
stype := reflect.TypeOf(s)
fmt.Println("TypeOf s:", stype)
svalue := reflect.ValueOf(s)
fmt.Println("ValueOf s:", svalue)
}
反射对象到对象
元数据
- 元数据就是 “数据的数据”
- 把对象的类型表示成一个数据类型
- 把对象的值表示成一个数据类型
func main() {
s := "go"
// 对象的类型
// 把对象的类型表示成一个接口
stype := reflect.TypeOf(s)
fmt.Println("TypeOf s:", stype) // TypeOf s: string
// 对象的值
// 把对象的值表示成一个接口
svalue := reflect.ValueOf(s)
fmt.Println("ValueOf s:", svalue) // ValueOf s: go
s2 := svalue.Interface().(string)
//
fmt.Println("s2:", s2) // s2: go
}
- runtime.eface 是运行时对空接口的表示
- runtime.emptyinterface 是 reflect 包对空接口的表示
- 对象到反射对象时,编译器会入参提前转换为 eface
- 反射对对象到对象时,根据地址和类型还原数据
反射调用方法
通过反射调用方法,实现用户方法和框架解耦
func MyAdd(a, b int) int {
return a + b }
func CallAdd(f func(a int, b int) int) {
v := reflect.ValueOf(f)
if v.Kind() != reflect.Func {
return
}
argv := make([]reflect.Value, 2)
argv[0] = reflect.ValueOf(1)
argv[1] = reflect.ValueOf(1)
result := v.Call(argv)
fmt.Println(result[0].Int())
}
func main() {
CallAdd(MyAdd)
}