这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
青训营掘金Day02-Go语言进阶与依赖管理
1.语言进阶
- 并发与并行区别
- go语言就是并发而生的
- 协程和线程区别
协程用户态由go语言去创建。
如何开启协程
快速打印Goroutine
通过go来开启协程
package main
import (
"fmt"
"time"
)
func hello(i int) {
println("hello goroutine: " + fmt.Sprint(i))
}
func main() {
for i := 0; i < 5; i++ {
go func(j int) {
hello(j)
}(i)
}
time.Sleep(time.Second)
}
协程间怎么通信
- 提倡通过通信共享内存而不是通过共享内存而实现通信
- 通过通道channel 来通信
Channel
make(chan 元素类型,[ 缓存大小 ])
- 无缓冲通道 make(chan int)
- 有缓冲通道 make(chan int,2)
有缓冲通道其实看做是一个货架或者仓库,是有限的,当满了之后会阻塞发送。
package main
func main() {
src := make(chan int)
dest := make(chan int, 3)
// A
go func() {
defer close(src)
for i := 0; i < 10; i++ {
src <- i
}
}()
// B
go func() {
defer close(dest)
for i := range src {
dest <- i * i
}
}()
// M
for i := range dest {
//复杂操作
println(i)
}
}
并发安全 Lock
go也保存对共享内存通信的机制
package main
import (
"sync"
"time"
)
var (
x int64
lock sync.Mutex
)
func addWithLock() {
for i := 0; i < 2000; i++ {
lock.Lock()
x += 1
lock.Unlock()
}
}
func addWriteoutLock() {
for i := 0; i < 2000; i++ {
x += 1
}
}
// 测试
func main() {
x = 0
// 没有加锁出现并发安全问题
for i := 0; i < 5; i++ {
go addWriteoutLock()
}
time.Sleep(time.Second)
println("WithOutLock:", x)
x = 0
for i := 0; i < 5; i++ {
go addWithLock()
}
time.Sleep(time.Second)
println("WithLock:", x)
}
WaitGroup
前面都用sleep来阻塞(不优雅)
计数器 开启协程+1;执行结束-1;主协程阻塞直到计数器为0。
package main
import (
"fmt"
"sync"
)
func hello1(i int) {
println("hello goroutine: " + fmt.Sprint(i))
}
func main() {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func(j int) {
defer wg.Done()
hello1(j)
}(i)
}
wg.Wait()
println("运行完成")
}
2.依赖管理
2.1背景
站在巨人的肩膀上
- 工程项目不可能基于标准库0~1编码搭建
- 管理依赖库
我们应该把更多精力关注在业务逻辑上,类似于java的Maven
2.2 依赖管理演进
- 不同环境(项目)依赖的版本不同
- 控制依赖库的版本
2.2.1 GOPATH
环境变量 $GOPATH,这个目录下面主要有三个文件夹bin、pkg、src
- bin 项目编译的二进制文件
- pkg 项目编译的中间产物、加速编译
- src 项目源码
- 项目代码直接依赖src下的代码
- go get 下载最新版本的包到src目录下
弊端
场景: A 和 B依赖于某一个package的不同版本
问题:无法实现package的多版本控制
2.2.2 Vendor
- 项目目录下增加 vendor文件,所有依赖包副本形式放在$ProjectRoot/vendor
- 依赖寻址方式:vendor => GOPATH
通过每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题。
弊端
问题:
- 无法控制依赖的版本
- 更新项目又可能出现依赖冲突,导致编译出错。
2.2.3 Go Module
- 通过 go.mod 文件管理依赖包版本
- 通过 go get/go mod 指令工具管理依赖包
终极目标:定义版本规则和管理项目依赖关系
2.2 依赖管理三要素
- 配置文件,描述依赖 go.mod
- 中心仓库管理依赖库 Proxy
- 本地工具 go get/mod
2.3 依赖配置
2.3.1 依赖配置-go.mod
依赖标识 :[Module Path][Version/Pseudo-version]
2.3.2 依赖配置-version
语义化版本 {MINOR}.${PATCH} V1.3.0 V2.3.0
- MAJOR: 不同的major间不兼容的,之间是代码隔离的
- MINOR: 新增函数或功能,需要在major下兼容
- PATCH: 修改bug
2.3.3 依赖配置-indirect
依赖关系 A->B->C: A->B 直接依赖,A->C 间接依赖
2.3.4 依赖配置-incompatible
- 主版本2+模块会在模块路径增加/vN 后缀
- 对于没有go.mod文件 并且主版本2+的依赖,会+incompatible
2.3.5 依赖分发-回源|Proxy
- 无法保证构建稳定性:增加/修改/删除软件版本
- 无法保证依赖可用性:删除软件
- 增加第三方压力:代码托管平台负载问题
通过一个Proxy代理来保证依赖的稳定性。
2.3.6 依赖分发-变量 GOPROXY
GOPROXY="proxy1.cn,https://proxy2.cn,…" 服务站点URL列表,"direct"表示源站
2.3.7 工具-go get
go-get是Golang的软件包管理工具。它允许用户下载、安装、更新和管理Golang项目中使用的第三方库。它与其他语言的包管理工具(如npm和pip)类似,可以自动处理依赖关系并确保所有库都是最新版本。
2.3.8 工具-go mod
Go Modules是Go语言的包管理工具,它是Go1.11版本中引入的一项功能。它支持管理Go程序的依赖关系,可以自动处理依赖关系并确保所有库都是最新版本。Go Modules可以让开发者管理本地和远程依赖关系,同时在项目中使用多种版本的库。它还支持在离线环境中工作,并具有版本控制和回滚功能。