1.0 从并发编程本质了解Go高性能的本质
1.1 Goroutine
协程可以理解为轻量级线程;
Go更适合高并发场景原因之一:Go语言一次可以创建上万协成;
“快速”:开多个协成 打印。
go func():在函数前加 go 代表 创建协程;
time.Sleep():协程阻塞,使主协程 在子协程结束前阻塞不退出;
乱序输出 说明并行;
1.2 协程通信 CSP (Communicating Sequential Processes)
通过通信共享内存:Channel通道遵循 先入先出 保证顺序
1.3Channel
Channel创建需要通过make()函数;
无缓冲通道也称 同步通道;有缓冲:缓冲满了之后要拿走才能存。
通过通信共享内存 例子:
M主函数作为消费者 实际中业务比较复杂,所以会比 生产者AB慢;我们用 带缓冲的通道 就不会因消费者M的消费速度 影响 生产者的执行效率;
1.4 并发安全Lock
addWithLock() 通过临界区控制实现;在 每次 x+=1前后加解锁;
addWithOutLock() 没加锁;
Add 测试函数;对两种实现做5个协程并发执行;
结果为:加锁时输出预期结果10000;体现不加锁的并发安全问题
1.5 WaitGroup实现并发任务的同步
计算器=0表示所有并发任务结束;
1.1阻塞的优化
好的,以上就是对go并发编程相关概念的介绍,这里简单做个小结
整个章节主要涉及3个方面,一个是协程,通过高效的调度模型实现高并发操作,一个是通道channel.通过通信实现共享内存;最后svnc相关关键字,实现并发安全操作和协程间的同步
2.0 依赖管理
这一章我们主要讲解go的依赖管理, 主要涉及go依赖管理的演进路线和go module实践
依赖指各种开发包
对于hello world以及类似的单体函数只需要依赖原生SDK,而实际工程会相对复杂,我们不可能基于标准库0~1编码搭建,而更多的关注业务逻辑的实现,而其他的涉及框架、日志、driver、以及collection等一系列依赖都会通过sdk的方式引入, 这样对依赖包的管理就显得尤为重要
2.1 Go依赖管理演进
2.1.1 GoPATH
GOPATH是Go语言支持的一个环境变量,value是Go项目的工作区。
目录有以下结构:
src: 存放Go项目的源码:
pkg: 存放编译的中间产物,加快编译速度;
bin: 存放Go项目编译生成的二进制文件,
大家想想用gopath依赖管理有 哪些弊端呢?
弊端
如图,同一个pkg,有2个版本,A-> A0,B-> B0.而src下只能有1个版本存在,那AB项目无法保证都能编译通过。
也就是在gopath管理模式下,如果多个项目依赖同一个库,则依赖该库是同一份代码,所以不同项目不能依赖同一个库的不同版本,这很显然不能满足我们的项目依赖需求。为了解决这问题,govender出现了
2.1.2 GoVendor
Vendor是当前项目中的一个目录,其中存放了当前项目依赖的副本,在Vendor机制下,如果当前项目存在Vendor目录,会优先使用该目录下的依赖,如果依赖不存在,会从GOPATH中寻找。
vendor无法很好解决依赖包的版本变动问题和一个项目依赖同一个包的不同版本的问题,下面我们看一个场景
如图项目A依赖pkg b和c,而B和C依赖了D的不同版本,通过vendor的管理模式我们不能很好的控制对于D的依赖版本,一旦更新项目,有可能带来依赖冲突。
归根结底vendor不能很清晰的标识依赖的版本概念原因是他还是依赖源码。下面,go mod就应运而生了。
2.1.3 Go Module
Go Modules 是Go语言官方推出的依赖管理系统,解决了之前依赖管理系统存在的诸如无法依赖同一个库的多个版本等问题
G0 module从1.11 开始实验性引入,1.16 默认开启;我们一般都读为go mod,我们也先统一下名称
2.2 依赖管理三要素
那其实完善的依赖管理一般都需要3要素,这里我们先整体介绍下
这里熟悉java的同学,可以类比下maven