并发编程
Goroutine
Go语言中引入了协程概念
func hello(i int) {
fmt.Println("hello goroutine: " + fmt.Sprint(i))
}
func HelloGoRoutine() {
for i := 0; i < 5; i++ {
// go 关键字就可以实现
go hello(i)
}
// 保证子协程执行完之前主协程不退出
time.Sleep(time.Second)
}
该程序会乱序打印出0-4这五个数字,goroutine只需要在函数前加关键字go即可
CSP
Communicating Sequential Processes
Go语言提倡通过通信共享内存而不是通过共享内存而实现通信
Channel
创建channel
make(chan, type, size)
如果是有缓冲通道,就可以指定size的大小,如果是无缓冲通道,则不设置size
下例为子协程发送0~9的数字,主协程输出最后的平方数
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 {
fmt.Println(i)
}
}
通过使用通道来保证了生产和消费。使用有缓冲区的dest是因为可能消费者的处理速度(处理逻辑)比较慢,需要使用带缓冲的。
并发安全
多个协程访问同一片内存可能导致访问不安全
var (
x int64
lock sync.Mutex
)
func addWithLock() {
for i := 0; i < 2000; i++ {
lock.Lock()
x += 1
lock.Unlock()
}
}
func addWithoutLock() {
for i := 0; i < 2000; i++ {
x += 1
}
}
func Add() {
x = 0
for i := 0; i < 5; i++ {
go addWithLock()
}
time.Sleep(time.Second)
fmt.Println("With lock result:", x) //输出结果为10000
x = 0
for i := 0; i < 5; i++ {
go addWithoutLock()
}
time.Sleep(time.Second)
fmt.Println("Without lock result:", x) //随机输出结果
}
通过观察输出结果可以看出,如果不使用lock,最终的结果是一个随机的结果,是因为五个协程并发访问内存区域x导致访问不安全。使用lock后,可以保证x在被一个协程访问的时候不会被其他的影响。
WaitGroup
WaitGroup可以代替第一个例子中的time.Sleep(time.Second)
是一个计数器,开启一个协程+1,执行结束-1,主协程阻塞直到计数器为0。
func hello(i int) {
fmt.Println("hello goroutine: " + fmt.Sprint(i))
}
func ManyGoWait() {
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()
}
依赖管理
Go Module
Go 1.11 版本推出 modules 机制,简称 mod,更加易于管理项目中所需要的模块。模块是存储在文件树中的 Go 包的集合,其根目录中包含 go.mod 文件。 go.mod 文件定义了模块的模块路径,它也是用于根目录的导入路径,以及它的依赖性要求。每个依赖性要求都被写为模块路径和特定语义版本。
通过go.mod文件管理依赖包版本
通过go get/go mod指令工具管理依赖包
go.mod
go.mod用于配置文件,描述依赖
模块路径用来标识一个模块,从模块路径可以看出从哪里找到该模块。如果是github前缀则表示可以从Github仓库找到该模块。
单元依赖中,每个依赖单元用模块路径+版本来唯一标识
单元依赖中的indirect表示非直接依赖
依赖分发
依赖分发指从哪里下载,如何下载
使用Github进行代码托管存在多个问题
使用Go Proxy可以解决上述问题。Go Proxy是一个服务站点,会缓存源站中的软件内容;使用Go Proxy之后,构建会直接从Go Proxy站点拉取依赖