Go 语言进阶 | 青训营笔记

62 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

今天主要的学习内容分为三个部分:

  1. 并发编程
  2. 依赖管理
  3. 单元测试

并发编程

Go 语言的并发程序可以用两种手段实现,其中一种就是 「顺序通信进程」(communicating sequential processes,CSP),使用 goroutine 和 channel 实现,另外一种是传统的多线程共享内存。

goroutine 和线程的区别主要在于一下几个方面:

  1. 动态栈,goroutine 为了减少内存浪费,在初始时只会分配 2KB 的栈空间,并且可以动态的伸缩。而线程的内存分配往往是 MB 级别的。
  2. OS线程会被操作系统内核调度。每隔几毫秒硬件计时器就会中断,如果线程被调度,那么就会进行寄存器的恢复,产生局部性比较差的内存访问,增加了CPU周期。但是 goroutine 本身提供了一个调度器,它不需要进入内核的上下文,所以调度 goroutine 的代价要比调度线程低很多。
  3. 线程都有一个独立的 ID 号,而 goroutine 却没有。

另外,goroutine 调度器使用 GOMAXPROCS 参数来控制会有多少个 OS 的线程同时执行 Go 的代码。

依赖管理

Go 依赖管理由 GOPATH 过渡到 Go Vendor,再过渡到 Go Module。

cd 到 $GOPATH 中,可以看到项目的代码,go get 可以下载最新版本的包到 src 目录中,bin 目录是项目的二进制文件,通常可以直接运行。GOPATH 的弊端是当两个 project 依赖不同的版本时,会遇到一些兼容性问题,无法实现 package 的多版本控制。

而 Go Vendor 给每个项目引入依赖的副本,这样可以解决不同项目之间的依赖版本问题。但是,如果多个项目直接有间接依赖版本不兼容问题,Go Vendor 也难以胜任。

Go Module 通过 go.mod 文件管理依赖包版本,然后使用 go get/go mod 指令工具管理依赖包。如果当前项目所依赖的两个项目使用了不同的版本依赖,那么会取满足兼容性的最低版本。

测试

测试的重要性不在多说,除了最常见的单元测试,还有集成测试和回归测试。覆盖了从左到右逐步减小,成本逐步升高。

对于单元测试,一般覆盖率在 50%~60%,比较高的时候可以到 80%+。在做单测时,要保证幂等性和稳定性。幂等是指每一次测试运行都应该产生与之前一样的结果,稳定是指相互隔离,能在任何时间任何环境中进行测试。

Mock 可以帮助测试更好的满足上面两个特性。

测试的另一个方面是基准测试,也是性能测试。可以帮助我们测试程序的运行效率等指标,进而对程序进行优化。