这是我参与【第五届青训营】伴学笔记创作活动的第七天,此篇概述一下自学的go的进阶知识点。
Go进阶知识点巩固
runtime包
常用函数:
runtime.GOROOT() // 获取 goroot 目录
runtime.GOOS // 获取操作系统
runtime.NumCPU() // 获取逻辑 cpu 的数量
runtime.NumGOMAXPROCS(runtim.NumCPU()) // 设置go程序执行的最大cpu的数量
runtime.Gosched() // 让出时间片,让其他的go协程先执行
runtime.Goexit() // 结束当前go协程,不影响defer函数的执行
临界资源
- 定义:指并发环境中多个进程/线程/协程共享的资源。
- 安全问题:借助于 sync 包下的锁操作(不建议,建议使用通道的方式)
sync 基础
-
sync 基础应用
// 同步等待组 var wg sync.WaitGroup // 创建同步等待组的对象 wg.Add() // 设置等待组要执行的子 goroutine 的数量 wg.Wait() // 让主 goroutine 处于等待 wg.Done() // 给 wg 等待组中的 counter 数值减 1 func main() { wg.Add(2) go func() { defer wg.Done() fmt.Println("1") }() go func() { defer wg.Done() fmt.Println("2") }() fmt.Println("wait!") wg.Wait() fmt.Println("finish....") } -
互斥锁
只能有一个 goroutine 占用这个锁
var mutex sync.Mutex // 创建一个互斥锁 mutex.Lock() // 上锁 mutex.Unlock() // 解锁 -
读写锁
var rwMutex *sync.RWMutex // 创建读写锁 rwMutex.RLock() // 读操作上锁 rwMutex.RUnlock() // 读操作解锁 rwMutex.Lock() // 写操作上锁 rwMutex.Unlock() // 写操作解锁
channel 通道
-
channel 非缓冲通道
ch := make(chan, int) // 默认是阻塞的
// 当互斥锁用 func main() { ch := make(chan bool) go func() { for i:=0; i<10; i++ { fmt.Println(i) } ch <- true }() <- ch fmt.Println("over") } close(ch) // 关闭通道 data, ok := <- ch // ok 若为 false则通道已关闭 // 安全的传递所有的数据 var ch chan int func main() { ch = make(chan int) go sendData() // 读取通道的数据 /*for { time.Sleep(1 * time.Second) v, ok := <-ch if !ok { fmt.Println("data over!....", ok) break } fmt.Println("读取的数据是:", v, ok) }*/ // 用 range 更好 for data:= range ch { fmt.Println("读取的数据是:",data) } fmt.Println("main over...") } func sendData() { defer close(ch) for i := 0; i < 10; i++ { ch <- i } } -
channel 缓冲通道
用法更灵活
ch := make(chan int, capacity) // 定义方法 -
channel 定向通道
即单向通道,只能读或写,上述都为双向通道。
ch := make(chan <- int) // 读通道 ch := make(<- chan int) // 写通道 // 简单用法 ch := make(chan int) f(ch) func f(ch1 chan <- int) { // 可传入 ch, 但限制为只读 }
Time 包中的通道相关函数
主要就是定时器,处理超时问题。
// NewTimer() 用法
timer := time.NewTimer(3*time.Second) // 创建一个计时器
time2 := <- timer.C // 结束时触发,返回当前时间
// After() 用法
timer := time.After(3*time.Second)
time2 := <- timer
go 的反射机制
-
反射初识:(reflect 包)
func TypeOf( x interface{ } ) Type 函数,可传入任意类型值,判断类型
func ValueOf(x interface{ } ) Value 函数
var x float64 = 3.4 // reflect.TypeOf(x) // reflect.ValueOf(x) v := reflect.ValueOf(x) fmt.Println("kind is float64: ", v.Kind()==reflect.Float64) fmt.Println("type: ", v.Type()) fmt.Println("value: ", v.Float())对于反射,一般非必要不要使用。最大的原因是编译发现错误需要时间久,以及性能影响大,运行效率相对较慢。
-
(静态类型+动态类型)
var A interface{} // 静态类型 interface{} A = 10 // 静态类型 interface{}, 动态类型 int // A = "has" // 可重新赋值为其他类型 -
反射的使用
-
从 reflect.Value 获取接口 interface 信息 realValue := value.Interface().(type)
-
获取字段
type Person struct { Name string Age int Sex string } func main() { p := Person{"小林", 18, "男"} Getmessage(p) } // 获取 input 信息 func Getmessage(input interface{}) { getType := reflect.TypeOf(input) // 先获取 input 的类型 // fmt.Println(getType.Name()) // Person // fmt.Println(getType.Kind()) // struct getValue := reflect.ValueOf(input) // {小林 18 男} // 获取字段 for i := 0; i < getType.NumField(); i++ { field := getType.Field(i) value := getValue.Field(i).Interface() fmt.Printf("字段名称:%s, 字段类型:%s, 字段数值:%v \n", field.Name, field.Type, value) } }
-
通过 reflect.Value 设置实际变量值
普通类型:
var num float64 = 1.23 pointer := reflect.ValueOf(&num) // 指针 newValue := pointer.Elem() // 指针指向的元素 newValue.CanSet() // 是否可以修改 newValue.SetFloat(3.14) // 根据数的类型设置值结构体:
type Person struct { Name string Age int Sex string } func main() { p := Person{"小林", 18, "男"} p1 := &p // 改变数值 value := reflect.ValueOf(&p) if value.Kind() == reflect.Ptr { newValue := value.Elem() f1 := newValue.FieldByName("Name") // 指定元素 f1.SetString("xiaming") // 设置值 } }方法调用:
func (p Person) PrintInfo() { fmt.Println("hahahahahah") } p := Person{"小林", 18, "男"} value := reflect.ValueOf(p) methodValue := value.MethodByName("PrintInfo") // 根据函数名获取函数 methodValue.Call(nil) // 没有参数直接调用 , nil 或者 空切片也行函数调用:
func f() {} value := reflect.ValueOf(f) value.Call(nil) // 同理
-