这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
一、并发编程
- 协程Goroutine:用户态,轻量级线程,栈KB级别,只需在用户态即可完成上下文的切换,使用
go关键字即可开启线程
func hello(i int) {
println("hello goroutine : " + fmt.Sprint(i))
}
func HelloGoRoutine() {
for i := 0; i < 5; i++ {
go func(j int) {
hello(j) //输出是无序的
}(i)
}
time.Sleep(time.Second)
}
-
Channel:通过通信共享内存
创建通道:make(chan 元素类型,[缓冲大小])
无缓冲通道:make(chan int)--同步
有缓冲通道:make(chan int,2) 通过
<-向通道内传递数据
func CalSquare() {
src := make(chan int)
dest := make(chan int, 3)
go func() {
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)
}
}
- 并发安全Lock:加锁确保原子性,定义一个变量类型为
sync.Mutex的锁,在需要使用锁的位置前后分别添加Lock()和Unlock()
var (
x int64
lock sync.Mutex
)
func addWithLock() {
for i := 0; i < 2000; i++ {
lock.Lock()
x += 1
lock.Unlock()
}
}
- 线程同步WaitGroup:WaitGroup等待goroutine的收集完成。主要的goroutine调用
Add来设置要等待的goroutine数量。然后运行每个goroutine,并在完成时调用Done。同时,可以使用Wait来阻塞,直到所有goroutine都完成
func ManyGoWait() {
var wg sync.WaitGroup
wq.Add(5)
for i := 0; i < 5; i++ {
go func(j int) {
defer wg.Done
hello(j) //输出是无序的
}(i)
}
wg.Wait()
}
二、依赖管理
依赖管理三要素
- 配置文件,描述依赖 go.mod
- 中心仓库管理依赖库 Proxy
- 本地工具 go get/mod
- Go Module
- 通过go.mod文件管理依赖包版本
- 通过go.get/go mod指令工具管理依赖包
- 依赖配置-version
- 语义化版本: V1.3.0 --大版本.新增版本前后兼容.bug修复
- 基于commit伪版本:vx.0.0-20020506112233-a4g6a -- 语义-时间戳-随机哈希值
- 依赖配置-indirect
- 标识没有直接导入的依赖模块
- 依赖配置-incompatible
- 可能会存在不兼容的代码逻辑
- 依赖分发-变量GOPROXY
- GOPROXY="proxy.cn,direct" ,"direct"表示源站
- 工具-go mod
- init ,初始化,创建go.mod文件
- download ,下载模块到本地缓存
- tidy ,增加需要的依赖,删除不需要的依赖
三、测试
单元测试
- 规则:所有测试文件以 _test.go 结尾
- func TestXxx(*testing.T)
- 初始化逻辑放到 TestMain 中
- assert:通过判断预期值与输出值是否相等,来确定是否出错
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestJudgePassLineTrue(t *testing.T) {
isPass := JudgePassLine(70)
assert.Equal(t, true, isPass)
}
- 覆盖率:
go test Xxx_test.go Xxx.go --cover - 技巧:测试分支相互独立、全面覆盖;测试粒度足够小,函数单一职责
- 依赖:幂等(多次运行结果不变)、稳定(独立运行)
MOCK测试
monkey: github.com/bouk/monkey
用一个函数替代另一个函数,实现打桩,减少依赖
monkey.Patch 打桩
defer monkey.Unpatch 对桩进行卸载
import (
"bou.ke/monkey"
"github.com/stretchr/testify/assert"
"testing"
)
func TestProcessFirstLine(t *testing.T) {
firstLine := ProcessFirstLine()
assert.Equal(t, "line00", firstLine)
}
func TestProcessFirstLineWithMock(t *testing.T) {
monkey.Patch(ReadFirstLine, func() string {
return "line110"
})
defer monkey.Unpatch(ReadFirstLine)
line := ProcessFirstLine()
assert.Equal(t, "line000", line)
}
基准测试
测试一段程序运行时的性能和CPU损耗
- 以前缀Benchmark标识一个基准测试
func BenchmarkXxx(*testing.B)
-
使用
go test Xxx.go -bench运行一个基准测试 -
基准测试函数必须运行目标代码 b.N 次。 在基准测试执行期间,b.N 被调整,直到基准测试函数持续 足够长,可以可靠地计时
func BenchmarkRandInt(b *testing.B) {
for i := 0; i < b.N; i++ {
rand.Int()
}
}
b.ResetTimer() :如果基准测试在运行前需要一些昂贵的设置,计时器可以重置
b.Next():下一步报告是否有更多迭代要执行