这是我参与「第五届青训营 」笔记创作活动的第2天
1 Go并发
第一天的课程中讲过,高性能、高并发是 Go 的特性之一。而本节课中则初步讲解了 Go 多线程的相关知识。Go 在语言层面支持并发,Go 内部实现了垃圾回收、内存共享等机制。goroutine 是轻量级线程,栈的大小是 KB 级别。
1.1 协程创建
Go 语言协程的开启十分简单,通过 go 关键字 + 函数名 开启 goroutine
go func(){
//do something
}
创建的协程返回的顺序是随机的,如执行如下代码
func main() {
for i := 0; i < 5; i++ {
go func(n int) {
fmt.Println(n)
}(i)
}
time.Sleep(2 * time.Second)
}
程序输出结果
4
0
3
1
2
如果不加 sleep 等待,则不会有输出。这是因为此时主线程结束,main 函数返回时不会等待其他协程而是会直接返回。另外,main 函数本身也是一个 goroutine。
1.2 WaitGroup
课程中还对此例子进行了拓展,除简单粗暴地使用 time.sleep( ) 方法外,还可以使用 WaitGroup 来等待进程结束。WaitGroup 类常用的方法有 Add( ) 用于增加计数器,Done( ) 用于协程结束后减少计数器,结合上一节课知识,可以放在defe内,Wait( ) 用于阻塞,等待协程结束直到计数器为0。
使用 WaitGroup 方法改造上例,如下:
func main() {
var wg sync.WaitGroup
wg.Add(5)
for i := 0; i < 5; i++ {
go func(n int) {
defer wg.Done()
fmt.Println(n)
}(i)
}
wg.Wait()
}
1.3 Channel
goroutine 线程间可以通过 Channel 进行通信,可以创建无缓存和有缓存的 channel ,需要使用 chan 关键字来创建。
ch <- v // 把 v 发送到通道 ch
v := <-ch // 从 ch 接收数据
make(chan int) //创建一个int类型的channel
make(chan int,2) //创建一个int类型,有2个单位大小缓冲区的channel
注意要用 close( ) 关闭管道,可以放在defer里。此外个人认为可以直接遍历管道这一点也十分方便
2 依赖管理
之前学习过程中了解过Java的依赖管理工具Maven,类似的,Go 也有依赖管理工具,目前主要历经了三个阶段:go path、go vendor 和 go mod。
使用go path 的方法相对简单,就是直接把所需的包下载到 gopath 的 src 目录下,直接依赖目录下的代码,但是若机器上存在两个不同的项目依赖同一个包的不同版本,此时无法满足需要。
使用go vendor 的方法解决了上述问题,在项目下新建 vendor 目录,存放依赖包的副本,项目使用时先找 vendor 再找 gopath。但是,同一项目的不同 package 仍有可能依赖同一包的不同版本,还是会有冲突。
使用 go mod 的方法解决了 go vendor 的问题。通过 go.mod 文件描述依赖,Proxy 作为中心仓库管理依赖库,可以通过 go get 和 go mod 等指令来安装和管理依赖