这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记
零、简介
本文根据字节跳动后端青训营第二节课 GO语言入门-工程实践 撰写,是本人记录的一些偏分散的知识点,阅读前请注意时效性,仅供参考。
一、语言进阶
1、并发VS并行
二者都是指多线程程序在CPU上运行,不同点在于并发是指多线程程序在一个核的CPU上运行,并行是指多线程程序在多个核的CPU上运行。 并行可以充分发挥多核优势,高效运行。
2、Goroutine
协程:用户态,轻量级,栈MB级别。 线程:内核态,线程跑多个协程,栈KB级别。
创建goroutine的方式:在函数前加上“go”
go func(j int) {
...
}(j)
3、CSP
CSP全称 Communicating Sequential Process 提倡通过通信共享内存而不是通过共享内存而实现通信,需要对临界区加锁实现。
4、Channel
channel就是通信的通道 创建方式如下:
chan := make(chan type, [size])
其中type表示通道的元素类型,size表示通道的缓冲大小,无缓冲通道则不需要设置缓冲大小。
往channel中输入数据如下:
chan <- val
channel遍历操作如下:
for i := range chan {
...
}
消费者处理速度比生产者慢时,带缓冲通道可避免影响生产者的运行效率。
5、并发安全与Lock锁
Lock用于处理临界区资源 创建方式:
var {
name type
...
lock sync.Mutex
}
其中name为需要加锁的变量名称,type为对应的变量类型
上锁与解锁:
lock.Lock()
...
lock.Unlock()
6、WaitGroup
实现并发任务的同步,内含计数器 创建方法:
var wg sync.WaitGroup
使用方法:
wg.Add(delta int) //计数器+delta
wg.Done() //计数器-1
//在协程中开头添加defer wg.Done()
Wait() //主协程阻塞,直到计数器为0为止
二、依赖管理
1、GOPATH
该目录三个主要文件夹
bin 项目编译的二进制文件 pkg 项目编译的中间产物,加速编译 src 项目源码
项目代码直接依赖 src 下的代码 go get 下载最新版本的包到 src 目录下
弊端
无法实现不同项目依赖同package的多版本控制。
2、GOVENDER
项目目录下添加Vender文件夹,存放依赖包副本,依赖寻址方式:vender -> GOPATH 解决了上文提到的依赖冲突问题。
弊端
无法实现不同子项目依赖同package的多版本控制。实际上依赖的是源码而不是版本。
3、GOMODULE
官方的依赖管理系统 通过go.mod文件管理依赖包版本 通过go get / go.mod 指令工具管理依赖包
3.1、管理依赖三要素
- 配置文件,描述依赖 go.mod 用以定位依赖
- 中心仓库管理 Proxy
- 本地工具 go get / mod
3.2、依赖配置
gomod文件:
module example/project/app //依赖管理基本单元 如:github.com/...
go 1.16 //原生库
require ( //单元依赖
example/lib1 v1.0.2
example/lib2 v1.0.4 //indrect
example/lib3 v1.0.6+incompatible
...
)
version
语义化版本: V{MAJOR}.{MINOR}.{PATCH} 如:V1.1.2 基于commit伪版本 vX.0.0-yyyymmddmmss(commit时间戳)-aaaabbbbcccc(hash)
indirect
如:A->B->C
- A->B 直接依赖
- A->C 简介依赖
incompatible
对于没有 go.mod文件且主版本2+的依赖的兼容标志
3.3、依赖分发GOPROXY
保证构建稳定,可用,解决代码托管平台负载问题
3.4、工具
go get
命令格式:
go get example.org/pkg ...
"...":
@update //默认
@none //删除依赖
@v1.1.2 //tag语义版本
@(hash) //特定的commit
@master //分支最新的commit
go mod
命令格式:
go mod ...
"...":
init //初始化,创建go.mod文件
download //下载模块到本地缓存
tidy //增加需要依赖,删除多余依赖
三、测试
1、单元测试
对测试单元输入数据,再将其实际输出与期望输出做对比进行校对的过程。单元可以是函数,模块等等。
- 所有测试文件以 *_test.go 结尾
- 测试函数命名为TestXxx(t *testing.T)
- 初始化逻辑放到TestMain(m *testing.M)中
func TestMain(m *testing.M) {
//数据装载
code := m.Run()
//释放资源
os.Eixt(code)
}
覆盖率
一般覆盖率:50%~60% 较高覆盖率:80%+
2、Mock
对单元打桩测试,由特定函数产生输入,不依赖本地文件。
3、基准测试
- 优化代码,需要对当前代码分析
- 内置的测试框架提供了基准测试的能力
- 以Benchmark开头的函数
- ResetTimer()重置计时