[并发编程及测试|青训营笔记]

119 阅读5分钟

这是我参与「第五届青训营」伴学笔记活动的第二天 今天我主要学习了协程,通道,并发编程,依赖,以及单元测试,mock测试和基准测试。

并发编程

并发多线程序在一个核的cpu运行,通过时间片的切换实现,而并行多线在多个核,是实现并发的一个手段,go通过最大调用效度利用资源

1.协程与线程

协程是用户态,可以看成轻量级线程,用go语言本身实现,是栈的KB级别,线程比较昂贵属于内核态,是一种系统操作,可以并发跑多个协程,是MB级别。go func开启协程 timesleep保证执行完一次主协程不退出。

image.png

2.CSP

在go中,协程更多的是通过通信共享内存,,通道先入先出保证收发顺序,让一个协程指令到另一个协程,但是go也保留了共享内存实现通信的机制。但是需要一个互斥量对内存进行加锁,也就是需要获取临界区的一个权限。最终可能影响性能。

3.Channel

通过make(chan类型,[缓冲大小])如果无缓冲通道则可省去缓冲大小。无缓冲通道会使收发两个协程同步化,因此该通道也被称为同步通道。如果想要解决这个问题需要添加缓冲区。下面是一个通道例子,该程序实现了两个协程收发计算0-9的平方数。

image.png

如上第一个gofunc完成了协程1发送0-9数字到src通道里,而第二个go func完成了协程2计算stc中的平方值并输出到有缓冲的dest通道里。最终在最后一个循环里完成了遍历dest中的数据并打印。能保证顺序,是并发安全的。

4.并发安全lock

但是有时候可能存在发送和接收速度不一致的问题,而带缓冲通道解决了该问题。下面程序展示了不加锁和加锁的的区别。可以看到不加锁会输出未知的结果,因此我们需要加锁对临界区进行控制来保证并发安全。

image.png

5.Waitgroup

通过waitgroup实现并发任务的同步。有add,done,wait三种方法。首先开启协程计数器加一,执行结束计数器减一。而wait函数用来阻塞主协程直到计数器为零。下面演示相关代码。这里wg.Add增加了5个协程,deter wg.Done对计数器减一,也就是结束任务已完成的一个协程,wg.wait进行阻塞。

image.png

依赖管理

依赖实质就是使用工具包。不同环境依赖的版本不同需要控制依赖库的版本。

1.GOPATH

GO项目工作区,bin为项目二进制生成的文件,pkg加速编译,src为项目源码,所有项目代码都直接依赖src,如果需要新包可以通过go get下载新版本的包到src目录下。但是会出现新旧版本无法兼容,即无法实现多版本控制。如果想要解决此类问题可以在项目目录下增加vendor文件,所有依赖包副本形式放在$ProjectRoot/vendor下。这样项目的依赖优先从vendor里进行寻找,如果没有再返回到GOPATH下。这样每个项目都有一份依赖库样本,便不会发生冲突。但是其依旧依赖项目源码,无法控制再上一级依赖的版本,更新项目后可能又出现依赖冲突,导致编译出错。

2.Go Module

我们还可以通过go.mod文件管理依赖包版本,最终完成定义版本规则和管理项目依赖关系。而这一依赖管理过程包含三要素,第一是使用go.mod配置文件描述依赖,第二步使用中心仓库proxy管理依赖库,第三步使用本地工具 get mod。module+文件路径表明所依赖的管理基本单元。使用go +版本号指定原生库,require为添加单元依赖

3.依赖配置

语义版本[大版本].[大版本].{新增函数功能(需要前后兼容)}${代码化的修复}

Commit伪版本:前缀(与语义化一样)—时间戳—12位的哈希校验码

没有直接导入的模块用//indirect在版本号后标示出来,incompatible

如果使用到同一个项目中的不同版本则优先选择最低的兼容版本

4.依赖分发

Proxy保证了依赖的稳定性,可以使用多个proxy

GOPROXY=”proxy1.cn,https://proxy2.cn,… “direct”表示源站

当有多个proxy会依次在多个proxy寻找依赖直到回到源站查找

5.go get工具

go get example.org/pkg+@update默认/@none删除依赖/@语义版本/@ commit版本/@master指定分支的最新commit。

go mod 中有init初始化,创建go.mod文件,download下载模块到本地缓存,tidy用来增加需要的依赖删除不需要的。

测试

1.单元测试

对测试单元的输出与期望进行校对,可以提高效率保证质量。所有测试文件以_test.go结尾。

func TestXxx(testing.T)需要大写,在func TestMain(mtesting.M)中进行初始化和测试后释放资源以下是单元测试的例子:

image.png

可以看到出现问题这时可以使用assert 。最后可以用覆盖率评价函数功能的完备性。一般50%左右。为了全面覆盖可以进行分支相互独立测试。因此我们的测试单元需要合理的小。

2.MOCK测试

幂等是指重复运行结果是否一样,打桩是指用一个函数替换另一个函数。使用mock进行打桩测试,从而去掉对本地文件的依赖性。Defer用来卸载桩函数。

3.基准测试

func BenchmarkXxx(b *testing.B)

b.RestTimer()重置时间

b.RunParallel实现并行

Func FastXxx函数进行优化

总结

今天的课程难度对我来说较大,一天光是理解和阅读相关文档都感觉不够充足,最主要的是还没有真正感受到这些工具的作用和意义,特别是测试阶段的mock还是没怎么弄懂原理。未来我还需要继续完成剩余的实战。