一、Go语言进阶和依赖管理
01、语言进阶
1.1 Goroutine
并发编程
多线程程序在一个核的CPU上运行
Go可以充分发挥多核优势,高效运行
协程
用户态,轻量级线程
package main
import "fmt"
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)
}
}
1.2 CSP(Communicating Sequential Processes)
提倡通过通信共享内存而不是通过共享内存而实现通信
1.3 Channel
通过make关键字创建
//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)
}
}
1.4 并发安全 Lock
-
当多个goroutine同时访问和操作共享的数据时,由于操作的顺序不确定,可能导致意外的结果。例如,两个goroutine同时对同一个变量进行写操作,结果可能出现数据损坏或不一致的情况。
-
在多个goroutine并发访问共享资源时,为了保证数据的一致性,通常需要使用互斥锁来进行同步。然而,互斥锁的错误使用可能导致死锁或活锁的情况,从而使程序无法继续执行。
1.5 WaitGroup
在Go语言中,WaitGroup是一种用于等待一组goroutine完成执行的同步机制。WaitGroup可以用于确保在所有的goroutine执行完成之后再继续执行后续的操作。
func ManyGoWait() {
var wg sync.WaitGroup
//使用Add方法增加计数器的值
wg.Add(5)
for i := 0; i < 5; i++ {
go func(j int) {
//调用Done方法减少计数器的值
defer wg.Done()
hello(j)
} (i)
}
//通过调用wait方法进行阻塞,直到计数器归零
wg.Wait()
}
02、依赖管理
GOPATH -> Go Vendor -> Go Module
2.1.1 GOPATH
- 所有项目代码直接依赖src下的代码
- go get下载最新版本的包到src目录下
弊端
在A、B项目同时依赖于某一package的不同版本时出现问题,无法实现package的多版本控制
2.1.2 Go Vendor
- 增加vendor文件,所有依赖包副本形式放在此处
- 依赖寻址方式:vendor => GOPATH
通过每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题
弊端
- 无法控制依赖的版本
- 更新项目又可能出现依赖冲突,导致编译出错
2.1.3 Go Module
- go.mod 文件管理依赖包的版本
2.2 依赖管理的三要素
1、配置文件,描述依赖 go.mod
2、中心仓库管理依赖库 Proxy
3、本地工具 go get/mod
2.3.1 依赖配置-go.mod
2.3.2 依赖配置-version
- 语义化版本
- 基于commit伪版本
2.3.3 依赖配置-indirect
a->b->c
ab之间直接依赖,ac直接间接依赖,对于不是直接依赖的依赖用indirect标识出来
2.3.4 依赖配置-依赖图
选择最低的兼容版本
2.3.5 依赖分发-回源
- 无法保证构建稳定性
- 无法保证依赖可用性
- 增加第三方压力
2.3.6 依赖分发-Proxy
2.3.7 工具-go get
2.3.8 工具-go mod
二、Go语言工程实践之测试
03、测试
测试是避免事故的最后一道屏障
3.1 单元测试
- 所有测试以_test.go 结尾
- func TestXxx(*testing.T)
- 初始化逻辑放到TestMain中
代码覆盖率
代码覆盖率是一种衡量测试覆盖程度的度量标准,用于确定在单元测试中有多少代码被执行过。它衡量了测试套件执行期间代码的执行路径,并告诉你你的测试是否覆盖了应用程序中的每个语句、分支、条件和边界情况。
在Go语言中,可以使用内置的测试工具go test来计算代码的覆盖率。
3.2 单元测试-依赖
幂等、稳定
3.3 单元测试-文件处理
3.4 单元测试-Mock
在编写单元测试时,有时我们需要模拟(Mock)一些依赖项或外部资源的行为,以便更好地控制测试环境并隔离被测试单元的逻辑。使用Mock可以帮助我们创建虚拟的对象,模拟预期的行为,并对被测试单元进行准确的验证。
3.5 基准测试
基准测试(Benchmarking)是一种评估程序性能的方法,用于确定代码在特定条件下的执行速度。在Go语言中,我们可以使用内置的基准测试框架来编写和运行基准测试。
基准测试的目的是衡量一段代码在给定输入下的执行时间。它对于性能优化和代码优化非常有用,可以帮助我们找到程序中的性能瓶颈和低效的部分。