这是我参与「第五届青训营」伴学笔记创作活动的第3天。今天复习了go的并发编程和测试相关内容,并将一些值得注意的点记录了下来。
协程
- 协程在用户态,线程在
- 内核态,协程比线程更加轻量。
- Golang 中的并发是函数相互独立运行的能力。Goroutines 是并发运行的函数。Golang 提供了 Goroutines 作为并发处理操作的一种方式。
- 创建一个协程非常简单,就是在一个任务函数前面添加一个go关键字。
通道
- Go提供了通道机制,用于在 goroutine 之间共享数据。
- 需要在声明通道时指定数据类型。在任何给定时间只有一个 goroutine 可以访问数据项,因此不会发生数据竞争。
- 根据数据交换的行为,有两种类型的通道:无缓冲通道和缓冲通道。无缓冲通道用于执行 goroutine 之间的同步通信,而缓冲通道用于执行异步通信。无缓冲通道保证在发送和接收发生的瞬间执行两个 goroutine 之间的交换。
- 通道语法:
- 创建:
Unbuffered := make(chan int) // 整型无缓冲通道 buffered := make(chan int, 10) // 整型有缓冲通道 - 通道值的发送和接收
goroutine := make(chan string,10) goroutine <- "China" data := <-goroutine
- 创建:
- 通道的发送和接收特性
- 对于同一个通道,发送操作之间是互斥的,接收操作之间也是互斥的。
- 发送操作和接收操作中对元素值的处理都是不可分割的。
- 发送操作在完全完成之前会被阻塞。接收操作也是如此。
WaitGroup实现同步
- 使用Add()来增加信号量,使用Done()来表示执行结束,释放信号量,使用Wait()来等待未执行结束的协程。
runtime包
- runtime包中定义了一些协程管理相关的api。
- runtime.Gosched():让出cpu时间片,重新等待安排任务。
- runtime.Goexit():退出当前协程。
- runtime.GOMAXPROCS:设置同时执行协程的数量。
Mutex互斥锁实现同步
- 除了使用通道实现同步之外,还可以使用Mutex互斥锁的方式实现同步。
var lock sync.WaitGroup lock.Lock() //加锁 lock.Unlock() //解锁
select
-
select是Go中的一个控制结构,类似于
switch语句,用于处理异步IO操作。select会监听case语句中channel的读写操作,当case中channel读写操作为非阻塞状态(即能读写)时,将会触发相应的动作。select中的case语句必须是一个channel操作
select中的default子句总是可运行的。
-
如果有多个
case都可以运行,select会随机公平地选出一个执行,其他不会执行。 -
如果没有可运行的
case语句,且有default语句,那么就会执行default的动作。 -
如果没有可运行的
case语句,且没有default语句,select将阻塞,直到某个case通信可以运行
测试
测试分类
- 回归测试:回归主流场景
- 集成测试:系统功能的自动化测试
- 单元测试:开发者对单独开发模块的验证
- 单元测试成本最高,但测试覆盖率也相对较高。单元测试可以保证质量,提升效率。
单元测试的规则
- 所有测试文件以
_test.go结尾 - 函数签名:
func TestXxx(t *testing.T) - 初始化逻辑放到
TestMain中:func TestMain(m *testing.M) { //测试前:数据装载、配置初始化等前置工作 code := m.Run() //跑包下的所有单测 //测试后:释放资源等收尾工作 os.Exit(code) }
单元测试覆盖率
- 即测试中实际run的代码行数占总代码行数的比例。
- 可以通过增加
--cover选项来打印单元测试覆盖率信息。 - 一般覆盖率:50%~60%,较高覆盖率80%+
- 测试分支要相互独立,全面覆盖
- 测试单元粒度要足够小,函数单一职责
单元测试依赖处理
- 幂等:重复处理case结果要相同
- 稳定: 单元测试相互隔离,独立运行
- mock机制:
- 为一个函数或者方法打桩,进行模拟,从而避免依赖外部环境
- 开源包:monkey : github.com/bouk/monkey
基准测试
- 优化代码,需要对当前代码进行分析
- 内置的测试框架提供了基准测试的能力
- 函数签名:
func BenchmarkXxx(b *testing.B)