Golang 指北
2022-09-23 15:31:35 0 举报
AI智能生成
登录查看完整内容
Golang 关键知识
作者其他创作
大纲/内容
``` go go func(msg string) { fmt.Println(msg) }(\"going\") ```
启用协程
``` go gopool.Go( func(){ /// do your job } ) ```
go
error 是go定义的接口,并未对其进行规范。
errors.New()
fmt.Errorf(\"\")
其中提到了几种 error 的规范方式。
if err == nil {} 判断
error
defer 的实现是由编译器和运行时共同完成的
经常被用于关闭文件描述符、关闭数据库连接以及释放资源
``` go tx := db.Begin() defer tx.Rollback() ```
只会在当前函数和方法返回之前被调用
先进后出 先声明的defer语句在后声明的之后执行。
作用域
defer关键字会立刻拷贝函数中引用的外部参数.
``` go func main() { startedAt := time.Now() defer fmt.Println(time.Since(startedAt)) time.Sleep(time.Second) } // $ go run main.go // 0s ```
``` go func main() { startedAt := time.Now() defer func() { fmt.Println(time.Since(startedAt)) }() time.Sleep(time.Second) } // go run main.go // 1s ```
虽然调用 defer 关键字时也使用值传递,但是因为拷贝的是函数指针,所以 time.Since(startedAt) 会在 main 函数返回前调用并打印出符合预期的结果。
预计算
数据结构
中间代码生成阶段的 cmd/compile/internal/gc.state.stmt 会负责处理程序中的 defer,该函数会根据条件的不同,使用三种不同的机制处理该关键字
执行机制
堆分配 (最初方式,性能最差)
栈分配 (1.13引入,性能替身百分之30)
有条件开启
编译期间判断 defer 关键字、return 语句的个数确定是否开启开放编码优化
如果函数中 defer 关键字的数量多于 8 个或者 defer 关键字处于 for 循环中,那么我们在这里都会禁用开放编码优化,使用上两节提到的方法处理 defer。
开放编码 (1.14引入,性能做好)
分配方式 (三种不同类型 defer 的设计与实现原理)
关键
与 recover 通常一起出现
调用 panic 后会立刻停止执行当前函数的剩余代码,并在**当前Goroutine** 中递归执行调用方的 defer;
recover 可以中止 panic 造成的程序崩溃。它是一个只能在 defer 中发挥作用的函数,在其他作用域中调用不会发挥作用;
``` go func panic(v interface{}) pannic(\"XXX 错误\") ```
``` go defer func() { if err1 := recover(); err1 != nil { log.GameSystemLog().Error(\"err........\
panic 与 recover
error ,defer 与 recover
Go 语言的 select 与操作系统中的 select 比较相似
与switch结构类似,但是 case 参数不同,select的必是 channel的收发 .
select 能在 Channel 上进行非阻塞的收发操作;
select 在遇到多个 Channel 同时响应时,会随机执行一种情况;
特点
空 select 会直接阻塞当前 Goroutine,导致 Goroutine 进入无法被唤醒的永久休眠状态。
直接阻塞
如果当前的 select 条件只包含一个 case 。
当 case 中的 Channel 是空指针时,会直接挂起当前 Goroutine 并陷入永久休眠。
单一管道
runtime.selectnbsend,它为我们提供了向 Channel 非阻塞地发送数据的能力
在不存在接收方或者缓冲区空间不足时,当前 Goroutine 都不会阻塞而是会直接返回
发送
从 Channel 中接收数据可能会返回一个或者两个值
runtime.selectnbrecv 会直接忽略返回的布尔值,而 runtime.selectnbrecv2 会将布尔值回传给调用方
接收
https://draveness.me/golang/docs/part2-foundation/ch05-keyword/golang-select/#%e5%b8%b8%e8%a7%81%e6%b5%81%e7%a8%8b
随机生成一个遍历的轮询顺序 pollOrder 并根据 Channel 地址生成锁定顺序 lockOrder;
根据 pollOrder 遍历所有的 case 查看是否有可以立刻处理的 Channel;
如果存在,直接获取 case 对应的索引并返回;
如果不存在,创建 runtime.sudog 结构体,将当前 Goroutine 加入到所有相关 Channel 的收发队列,并调用 runtime.gopark 挂起当前 Goroutine 等待调度器的唤醒;
当调度器唤醒当前 Goroutine 时,会再次按照 lockOrder 遍历所有的 case,从中查找需要被处理的 runtime.sudog 对应的索引;
在默认情况下,编译器会使用如下的流程处理 select 语句
非阻塞
阻塞
select
``` go for i := 0; i 10; i++ { println(i) } ```
fori(经典循环)
简单的经典循环相比,范围循环在 Go 语言中更常见。
编译器会在编译期间将所有 for-range 循环变成经典循环。
range (范围循环)
array / slice
``` go str := \"12345\
string
``` go hash := map[string]int{ \"1\
map
channel
for 与 range
先从 Channel 读取数据的 Goroutine 会先接收到数据;
先向 Channel 发送数据的 Goroutine 会得到先发送数据的权利;
概述 : channel是一个缓冲器,遵循先入先出的设计。
channel的容量会使得其长度为0(同步管道),长度大于0(异步管道)
特殊:chan struct{} 类型的异步 Channel — struct{} 类型不占用内存空间,不需要实现缓冲区和直接发送(Handoff)的语义;
异步与同步
当存在等待的接收者时,通过 runtime.send 直接将数据发送给阻塞的接收者;
当缓冲区存在空余空间时,将发送的数据写入 Channel 的缓冲区;
当不存在缓冲区或者缓冲区已满时,等待其他 Goroutine 从 Channel 接收数据;
直接发送
阻塞发送
ch - i 发送数据
直接接收
阻塞接收
close(ch)
关闭 channel
无锁管道
``` go state := 0 lock := &sync.Mutex{} for i := 1000; i 0; i-- { go func() { lock.Lock() defer lock.Unlock() state = state + 1 }() } time.Sleep(time.Second * 5) fmt.Println(state) ```
Mutex (互斥锁)
``` go // 只读的地方使用读锁 lock.RLock() // 有写的地方用写锁 lock.Lock() func TestXXXXX(t *testing.T) { state := 0 lock := &sync.RWMutex{} for i := 1000; i 0; i-- { go func() { lock.Lock() defer lock.Unlock() state = state + 1 }() go func() { lock.RLock() defer lock.RUnlock() fmt.Println(state) }() } time.Sleep(time.Second * 5) fmt.Println(\"xxxxxxxxxxxxxxxxx\") fmt.Println(state) ```
RWMutex (读写锁)
``` go wg := sync.WaitGroup{} wg.Add(3) // 调用 done 三次,wait 放行 for i := 1; i 4; i++ { go func() { time.Sleep(time.Duration(i) * time.Second) wg.Done() }() } wg.Wait() fmt.Println(\"over\") ```
WaitGroup
``` go // 保证在 Go 程序运行期间的某段代码只会执行一次 o := &sync.Once{} for i := 0; i 10; i++ { o.Do(func() { fmt.Println(\"only once\") }) } ```
once
Cond
context
并发
内存分配
栈内存管理
垃圾收集
GC内存
其实 go 的效率高很大程度上来源于 io 是非阻塞的,这一块可以看一下 go 的调度与网络轮训器实现,他们是go的根本。
其他
github
准备一个待测进程
gops
查看进程
本地进程
远端
trace
调优
KeyWords-Go
0 条评论
回复 删除
下一页