advice
- 使用time.Duration代替int64
- 为整数常量实现Stringer
- 每个阻塞或者io操作都应该有可取消或可超时
- 不要给枚举使用类型别名这会打破类型安全, 要重新定义类型
- 两个空结构体的地址可能相同但是比较时可能不同 (UB)
- slice, map, chan零值是nil, 内置函数是类型安全的
- 文件操作使用os.FileMode而不是使用 0777
- 不要依赖计算顺序, return res, json.Unmarshal(b, &res)不要这样做
- 在结构体定义中加入
_ struct{}
可以防止纯值初始化A{1, 2, 3}
这样会失败 - 在结构体中加入
_ [0]func()
可以防止使用==
比较两个结构体的值 - 管道应该由创建者关闭, 向一个关闭的管道发送数据会
panic
, 但是读不会. - 总是要关闭http body defer r.Body.Close() 否则会导致泄露, 因为会服用goroutine所以泄露的少
- 不要过度使用fmt.Sprintf("%s%s", a, b)
- 如果不要body考虑io.Copy(ioutil.Discard, resp.Body) http客户端不会重用链接直到body被读完.
- 不要在循环中使用defer
- 不要忘记停止time.Ticker
- 清空map的方式1. for k in m delete(m, k) 2. m = make(map)
内存逃逸
使用go build -gcflags=-m
引起逃逸的情况
- 在方法内将局部变量通过指针返回
- 发送指针或带有指针的值到channel中, 编译时没法知道那个goroutine会在channel上接收数据, 所以编译器没法知道变量什么时候被释放
- 在一个切片上存储指针或者带指针的值, 会导致切片的内容逃逸到堆上
- slice数组被重新分配时
- 在interface类型上调用方法, 在接口类型上调用方法都是动态调度, 方法真正的实现只有在运行时知道
字符串转成byte数组是否会发生内存拷贝
字符串转成切片会发生内存拷贝, 只要发生强制类型转换都会发生内存拷贝
如何在转换时不发生内存拷贝?
1package main
2
3import "reflect"
4import "unsafe"
5
6func main() {
7 a := "aaa"
8 stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&a))
9 b := *(*[]byte)(unsafe.Pointer(stringHeader))
10 _ = b
11}
GMP
GMP代表了三个角色 G: Goroutine 通过go关键字创建的执行体, 对应一个结构体g, 结构体保存了goroutine的堆栈信息 P: Process 代表处理器, 建立起了G和M的联系 M: Machine 代表操作系统的线程
goroutine由一个runtime.go(type g struct)的结构体表示, 主要存储执行的堆栈状态, 当前占用的线程, 调度等数据
goroutine的调度数据存储在sched.go, 在协程切换, 恢复上下文时用到
M对应操作系统的线程数, 由GOMAXPROCS控制(type m struct) 结构体中两个比较重要的东西 g0 表示深度参与运行时的调度过程, 比如创建, 内存非陪等. curg表示正在执行的goroutine, p表示正在运行代码的处理器, nextp, oldp 前一个和后一个处理器.
processor负责G和M的连接, 提供线程需要的上下文环境, 也能分配G到对应的线程上执行(type p struct)存储了性能追踪, 垃圾回收, 处理器的待运行队列, 计时器等.
1 GlobalQ <- g g g
2 g g g
3 g g g
4 Q Q Q // 当p的队列满时g加入全局的队列
5 ↓ ↓ ↓
6 p p p
7 ↓ ↓ ↓
8 m m m
调度策略
- 调度时机
- 调度策略
- 切换机制
CAS操作和ABA问题
cas步骤: 1.内存地址V 2.旧的预期值A 3.新值B 当且仅当地址V的值是预期值A时才更新成B值 ABA问题: 指在cas操作时其他线程将变量从A改成B但又改回成A, 此时其他线程在执行cas时又会改成B了. 解决方法: 增加版本号每次更新时版本号+1, 即A->B->C 变成 1A->2B->3C
http request 中Form的解析
|动作 | 函数 | 是否读取url | 是否读取body | 支持文本 | 支持二进制 | |Form |ParseForm() | Y | Y | Y | | |PostFrom |ParseForm() | | Y | Y | | |FormValue |ParseForm() | Y | Y | Y | | |PostFormValue |ParseForm() | | Y | Y | | |MultipartForm |ParseMultipartForm() | | Y | Y | Y | |FormFile |ParseMultipartForm() | | Y | | Y |
x-www-form-urlencoded
每个键值对在body中的形式和url中的query一样
form-data
: 每个键值对在body中都会用boundary隔开, body中的格式为----xxxxx
开头Content-Disposition: form-data; name=xxx\n\n
结尾是------xxxx--
, boundary在Content-Type
字段中