这是我参与「第五届青训营 」笔记创作活动的第2天
goroutine
协程:用户态,线程跑多个协程,栈KB级别,不需要系统调用,可以在代码中实现协程创建,多协程主要用于并发
线程:内核态,栈MB级别,需要系统调用创建,多线程主要用于并行
go语言中可以创建轻量级线程goroutine,创建方式有两个
1.
go 函数名(参数列表)
2.
go func(参数列表){
函数体
}(调用时传入的参数列表)
CSP
CSP全称Communicating Sequential Processes,翻译过来是“通讯顺序进程”,GO语言的CSP是通过goroutine和channel来实现的,两个线程实现通信的方式有两种:1.通过共享内存来实现通信 2.通过通信来共享内存。GO语言官方推荐使用第二种方式来实现线程之间的通信。不同的线程之间使用channel来交换数据。
channel
之前的切片和map是用make函数生成的,channel也是使用这个函数。
生成方法:make(chan 元素类型, [缓冲大小])缓冲大小是可选项,如果不填表示没有缓冲。
无缓冲区channel:
send数据到无缓冲区channel,如果没有协程取出数据,那么无缓冲区channel会阻塞,直到数据被取出
有缓冲区channel:
缓冲区满了才会阻塞,缓冲区有空间的时候正常运行
使用channel在不同的协程之间交换数据并处理,并在主线程中输出结果的示例如下:
func main() {
routine1 := make(chan int)
routine2 := make(chan int, 2)
go func() {
defer close(routine1)
for i := 0; i < 10; i++ {
routine1 <- i
}
}()
go func() {
defer close(routine2)
for j := range routine1 {
j := j * j
routine2 <- j
}
}()
for res := range routine2 {
fmt.Println(res)
}
}
输出的结果如下:
如果在两个协程内不加上defer close,将会抛出fatal error,警告程序出现了死锁,并给出死锁的goroutine。
GO语言也提供锁变量,变量类型sync.Mutex
waitGroup
用来管理协程运行,创建协程的时候让计数器增加(调用Add()方法),协程运行完之后让计数器减少(调用Done()方法),Wait()方法只有在计数器是0的时候才会取消阻塞。示例如下:
func hello(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()
hello(j)
}(i)
}
wg.Wait()
}
依赖管理
演变过程:
GOPATH -> go vender -> go mod
现在使用的是go mod来管理依赖
GOPATH
GOPATH路径下有三个文件夹,bin文件夹存放编译的二进制文件;pkg存放编译的中间产物,加速编译;src存放项目源码;项目代码直接依赖于src文件夹中的代码,go get下载的包放到src文件夹中
弊端:
多个项目依赖同一个包的不同版本的时候会出现问题,所以引入go vender
go vender
弊端:
如果一个项目下的两个包依赖于相同的包的不同版本,并且这两个版本不兼容,那么会导致编译错误,所以引入go mod
go mod
通过 go mod 文件管理依赖包版本
通过 go get/mod 指令来管理依赖包
module那一行表示依赖管理基本单元
go那一行表示原生库,编译的时候使用的go语言版本
require内的表示单元依赖,第一个参数是module path,在path后面加上/vn表示主版本至少是n;第二个参数是版本,后面的indirect表示间接依赖,+incompatible表示没有go.mod 文件并且主版本高于此版本
version表示方式:
编译的时候程序会根据依赖关系选择最低的兼容版本
依赖分发
如果直接依赖第三方库管理网站,有以下三个问题:
- 无法保证构建稳定性
- 无法保证依赖可用性
- 增加第三方平台的压力
在第三方平台和开发者之间加入一个proxy来管理各种依赖包,可以修改go env中的GOPROXY来配置proxy站点,direct表示源站点