Go语言进阶与依赖管理
一.语言进阶
1. 并发VS并行
(1)并发:多线程程序在一个核的CPU上运行
(2)并行:多线程程序在多个核的CPU上运行
Go语言实现了一个并发性能极高的调度模型,通过调度,可以最大限度的利用计算资源,充分发挥多核计算机的优势。
2.协程(Goroutine)
协程:用户态,轻量级线程,栈KB级别
线程:内核态,线程跑多个协程,栈MB级别
协程的创建和调度由Go语言本身来完成,线程上可以并发的跑多个协程,这就是Go语言适合高并发场景的原因。
可以通过一个go关键字来调用一个协程
3.CSP
Go语言提倡通过通信共享内存而不是通过共享内存实现通信
4.Channel
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)
}
}
func main() {
CalSquare()
}
A子协程发送0~9数字
B子协程计算输入数字的平方
主协程输出最后的平方数
题解:
1.创建了一个无缓冲的整数通道src
2.创建了一个缓冲容量为3的整数通道dest
3.A子协程通过一个for循环,将0到9的整数,发送到src通道
4.B子协程通过遍历src通达,将其中的数字平方后,发送到dest通道
5.最后主协程用于将dest通道中的平方结果输出
注解:defer close() 用于确保子协程在退出时,关闭相应的通道。
使用带缓冲的通道可以解决效率问题
二.依赖管理
1.Go依赖管理的3个阶段
- GOPATH
- Go Vendor
- Go Module
目的:
1.不同环境(项目)依赖的版本不同
2.控制依赖库的版本
(1)GOPATH环境变量
bin:项目编译的二进制文件
pkg:项目编译的中间产物,加速编译
src:项目源码
项目代码会直接依赖src下的代码
通过go get 下载最新的包到
弊端:无法实现package的多版本控制
(2) Go Vendor
项目目录下增加vendor文件,用于存放依赖包的副本
通过每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题
弊端:
1.无法控制依赖的版本
2.更新项目又可能出现依赖冲突,导致编译出错
(3)Go Module
1.通过go,mod文件管理依赖包版本
2.通过go mod/go get 指令工具管理依赖包
2.依赖管理的三要素
1.配置文件,描述依赖 go.mog
2.中心仓库管理依赖库 Proxy
3.本地工具 go get / go mod
3.version
(1)语义化版本
{minor}.${patch}
不同的major可以是不兼容的
minor通常是一些新增函数或功能,需要在major下做到前后兼容
patch一般是代码bug的修复
(2)基于commit伪版本
vX.0.0 - yyyymmddhhmmss - abcdefgh1234
"vX.0.0"为版本前缀
"yyyymmddhhmmss"为提交某次commit的时间戳
"abcdefgh1234"为12位的哈希校验码
GOPROXY变量
GOPROXY="proxy1.cn, direct"
服务站点URL列表 direct表示源站
若服务站点不存在依赖,就会到第三方的源站查找相关依赖
| go get example.org/pkg | @update | 默认 |
|---|---|---|
| go get example.org/pkg | @none | 删除依赖 |
| go get example.org/pkg | @v1.1.2 | tag版本,语义版本 |
| go get example.org/pkg | @23dfdd5 | 特定的commit |
| go get example.org/pkg | @master | 分支的最新commit |
| go mod | init | 初始化,创建go.mod文件 |
|---|---|---|
| go mod | download | 下载模块到本地缓存 |
| go mod | tidy | 增加需要的依赖,删除不需要的依赖 |
三.测试
测试是避免事故的最后一道屏障
测试包括回归测试、集成测试、单元测试,覆盖率逐渐变大,成本逐渐降低
1.单元测试
单元测试的规则:
1.所有测试文件以_test.go结尾
2.func TestXxx(*testing.T)
3.初始化逻辑放到TestMain中
一般覆盖率:50%~60%,较高覆盖率80%+
测试分支相互独立、全面覆盖
测试单元粒度足够小,函数单一职责
2.Mock测试:
3.基准测试:
1.优化代码,需要对当前代码分析
2.内置的测试框架提供了基准测试的能力
四.思考与总结
1. 依赖管理:
- Go语言使用`go.mod`文件来管理依赖。这是从Go 1.11版本引入的特性,它允许开发者声明项目的依赖,并确保依赖的版本一致性。
- 使用`go get`命令可以添加、更新或移除依赖。`go mod tidy`命令可以整理`go.mod`文件,确保依赖的准确性。
- 依赖管理的最佳实践包括定期更新依赖、使用语义化版本控制、避免不必要的依赖以及保持依赖的简洁性。
2. 并发编程:
- Go语言以其原生的并发支持而闻名,通过`goroutine`和`channel`实现。
- `goroutine`是轻量级的线程,可以在单个进程中并发执行。`channel`用于在`goroutine`之间通信和同步。
- 使用`sync`包中的工具,如`WaitGroup`和`Mutex`,可以实现对共享资源的同步访问。
3. 测试与调试:
- Go语言提供了内置的测试框架`testing`,用于编写单元测试和性能测试。
- 编写测试用例是确保代码质量和稳定性的关键。使用`go test`命令可以运行测试。
- 调试工具如`delve`可以帮助开发者在运行时检查和调试代码。
通过不断学习和实践,开发者可以更好地掌握Go语言的高级特性和依赖管理,从而构建高效、可靠的应用程序。