多线程
协程
协程: 用户态, 轻量级线程, 栈KB级别 协程: 内核态, 线程跑多个协程, 栈MB级别 使用go关键字来开启一个协程
通道
提倡通过通信共享内存, 而不是通过共享内存实现通信
make(chan 元素类型,[缓冲大小]
- 无缓冲通道(同步通道) make(chan int)
- 有缓冲通道 make(chan int,2)
func CalSquare() {
src := make(chan int)
dest := make(chan int, 1)
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 {
time.Sleep(3000000000)
println(i)
}
}
并发安全 Lock
lock sync.Mutex 对于共享内存来说, 有可能出现并发安全问题, 所以要对其加锁
WaitGroup
使用time.sleep时并不能确定应当让线程暂停的时间, 从而实现线程同步
| Add(delta int) | 计数器+delta |
|---|---|
| Done() | 计数器-1 |
| Wait() | 阻塞知道计数器=0 |
| 计数器: 开启协程+1, 执行结束-1; 主协程阻塞知道计数器为0 |
func CalSquare() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
go func(j int) {
defer wg.Done()
println(j)
}(i)
}
wg.Wait()
}
依赖管理
GOPATH->GO VENDOR -> GO Module
环境变量GOPATH
| |-- bin 项目编译的二进制文件 |-- pkg 项目编译的中间产物, 加速编译 |-- src 项目源码
- 项目源码直接依赖src下的代码
- go get 下载最新版本的包到src目录下 弊端
- 无法实现package的多版本控制
GO VENDOR
解决无法实现package的多版本控制的问题 | |-- Readme.md |-- dao |-- handler |-- mian.go |-- service |-- vendor
- 项目目录下增加vendor文件, 所有依赖包副本形式放在$ProjectRoot/vendor
- 依赖寻址方式: vendor -> GOPATH 通过每个项目引入一份依赖的副本, 解决了多个项目需要同一个package依赖冲突问题 弊端
- 无法控制依赖的版本
- 更新项目可能出现依赖冲突, 导致编译出错
GO Module
- 通过go.mod文件管理依赖包版本
- 通过go get/go mod 指令工具管理依赖包 类比maven
module github.com/wangkechun/go-by-example 依赖管理基本单元
go 1.18 原生库
require ( 单元依赖
example/lib1 v1.0.2
)
version
语义化版本
${MAJOR(大版本)}${MINOR(新增函数/功能)}${PATCH(修改bug)}
V1.3.0
V2.3.0
基于commit的伪版本
vx.0.0-yyyymmddhhmmss-${12位hash码前缀}
- 主版本2+模块会在模块路径增加/vN 后缀
- 对于没用go.mod文件并且主版本2+的依赖, 会+incompatible go在遇到依赖了同一个模块的不同版本时, 会选择依赖最低的兼容版本
go get
go get example.org/pkg +
| @update | 默认 |
|---|---|
| @none | 删除依赖 |
| @v1.2.2 | tag版本,语义版本 |
| @23dfdd5 | 特定的commit |
| @master | 分支的最新commit |
go mod
go mod +
| init | 初始化, 创建go.mod |
|---|---|
| download | 下载模块到本地缓存 |
| tidy | 增加需要的依赖, 删除不需要的依赖 |
总结与思考
协程
用户态协程:轻量级线程,栈大小为KB级别。
内核态协程:运行在内核态的线程上,栈大小为MB级别。
Go中的协程:使用go关键字启动一个新的协程。
通道
通信方式:提倡通过通信共享内存,而不是通过共享内存实现通信。
通道创建:
无缓冲通道:make(chan int)
有缓冲通道:make(chan int, 2)