这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
1. Goroutine
1.1 Why Goroutine
线程属于内核态,栈内核态;
协程是轻量级线程, 栈kB级 属于用户态
如下代码: 直接使用关键词 go 可以直接创建一个协程
func hello(i int) {
defer fmt.Println("hello, ", i)
}
func main() {
for i := 0; i < 5; i++ {
//go hello(i)
go func(j int) {
hello(j)
}(i)
}
time.Sleep(time.Second)
}
1.2 CSP -- goroutine communication
建议使用通信共享内存而不是通过共享内存实现通信,共享内存属于临界区,访问时需要加锁,会影响协程并发效率
1.3 Channel
如下图, 无缓冲通道和有缓冲通道,原理类似于操作系统的单缓冲区和双缓冲区
语法: make(chan 元素类型, [缓冲大小])
示例代码:
func CalSquare() {
src := make(chan int)
dest := make(chan int, 3)
go func() {
//defer 关键词的作用是 在同一作用域内代码执行完后执行该语句
defer close(src)
for i := 0; i < 10; i++ {
src <- i
}
}()
go func() {
defer close(dest)
for i := range src {
dest <- i * i
}
}()
for i := range dest {
println(i)
}
}
1.4 Lock 临界区
协程共享资源,互斥访问时,需要加锁
如图所示,如果对于共享资源 x 不加锁访问,下面例子会发生写后写的错误,导致最后结果与我们预期不相符。
1.5 waitGroup
sync.WaitGroup 计数器包括如下三个操作函数
如下示例中 time.Sleep() 函数可以由waitGroup 代替
2. 如何管理依赖库: GOPATH --> GO Vendor --> Go Model
2.1 GOPATH:
环境变量 GOPATH 是 go 项目的依赖库路径, 可通过 go env 查看或修改 $GOPATH 变量。
$GOPATH 下的三个目录作用如下所示:
弊端:
2.2 GO Vendor:
-
项目目录下增加 vendor 文件, 所有依赖包副本形式放在 ProjectRoot/vendor
-
依赖寻址方式: vendor => GOPATH
每个项目引入一份依赖的副本(vendor),解决了多个项目中需要同一个package依赖的冲突问题。
弊端:
- 无法控制依赖的版本
- 更新项目又可能出现依赖冲突,导致编译错误
2.3 Go Module
目标 -- 定义版本规则和管理项目依赖关系
- 通过 go.mod 文件管理依赖包版本
- 通过 go get/ go mod 指令工具管理依赖包
2.3.1 依赖管理三要素
-
配置文件,描述依赖 go.mod
-
中心仓库管理依赖库 Proxy -- 远程代理库
-
本地工具 go get/mod 指令
- 主版本2+模块会在模块路径增加 /vN 后缀
- 对于没有 go.mod 文件并且主版本2+的依赖,会 + incompatible
2.3.2 版本格式
2.3.3 依赖分发
2.3.4 工具
3.测试
- 回归测试:抖音刷视频,点赞等测试
- 集成测试: 自动化功能接口测试
- 单元测试: 具体到每个函数模块
3.1 单元测试
assert & go test :
编码原则:
编写函数应该尽量小,单一职责原则
3.2 外部依赖测试
3.2.1: 打桩 Mock
打桩后,使得函数不再被外部依赖影响
三方依赖: github.com/bouk/monkey
3.3 基准测试
go test -bench=.
测试性能,热点代码