这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天
本节课的重点内容如下:
- 学习go的并发基础,如goroutine、channel、lock等
- 学习go的依赖管理机制以及其发展历程
- 学习使用go进行代码测试(没写在笔记中
- 项目实践,使用gin框架实现一个web接口(没写在笔记中
go并发基础
1、goroutine(go的协程)
协程是线程中的特殊执行体(也可以说是特殊函数) ,一个线程可以创建多个协程,线程在创建协程的时候会为它分配用户栈空间,多个协程共享所属线程的资源
一个线程同时只能执行一个协程,协程在执行的过程中可以主动让出cpu资源,主动切换(这部分由程序员控制协程的切换逻辑,所以会更加方便,并且协程切换是发生在用户态的、协程切换要切换的资源也较少,也就导致它的性能比线程更高) ,从而让线程调度其他协程继续执行。
引入协程,就能够以少量的线程资源,实现高并发的效果
2、go协程之间的通信方式
1)通过通道channel来通信
2)通过共享内存(临界区)来通信(使用临界区,需要加锁等操作,开销较大)
3、通道channel
4、Lock互斥锁(适用于使用共享内存的场景)
5、WaitGroup同步等待组(适合于协程间的同步,不需要临界区等资源)
WaitGroup内部会维护一个计数器,当计数器为0时,才可以执行wg.Wait()之后的语句,常见的用法在于等待指定数量的协程关闭后,再执行相应的逻辑
例子如下:
启动了两个协程,使用wg.Add(2),添加2个计数
在协程执行完毕时,使用wg.Done()进行计数-1
主协程调用wg.Wait()阻塞等待所有协程关闭后,再执行下面的逻辑
依赖管理
1、GOPATH
bin: 用来存放项目编译后产生的可执行文件
pkg: 在项目编译后会产生一些静态库文件(后缀为.a),在编译和链接程序时能够加速编译
src: 用于存放 Go 源码文件,包括我们自己编写的源码以及使用 go get 命令所下载的依赖包的源码都是存放在这里的。
弊端
当两个本地项目同时依赖于同一依赖包的不同版本时
比如说以下情况:
项目A使用了v1版本的依赖包,会使用到里面的func A()函数
项目B使用了v2版本的依赖包,v2版本将func A()函数替换为了func B()函数,而项目B中使用了B函数
也就是说,A和B两个项目当前是无法同时编译运行的,会产生代码冲突;或者说A项目是无法编译运行的
原因:
项目B使用了V2版本的依赖包,也就是说依赖包经过了更新,新版本的包替换了src目录下的旧包(src目录下只能同时存在一个版本的依赖包)
所以项目A在编译时,就会使用新版本的依赖包进行编译,然后又会因为包中没有func A()函数而编译失败
也就是说,GOPATH无法解决本地多个项目对于同一依赖包的多版本依赖问题;使用git多人开发同一项目也同样会出现这种问题,GOPATH也无法解决
总而言之:GOPATH并不在意依赖包的版本,所以无法解决多版本所带来的各种问题
为了解决这个弊端,引入了新的依赖管理机制 —— Go Vendor
2、Go Vendor
本质上,就是在每个项目的目录下增加一个vendor目录,然后将项目所使用的依赖包,以副本的形式保存在vendor目录下
所以每次编译项目,都会先从vendor目录中找有无依赖包,若无则再去GOPATH下找
弊端
尽管Go Vendor从表面上看起来解决了问题,但实际上并无法解决嵌套的多版本依赖问题
根本的问题在于:Vendor只是粗暴地保存了依赖包的副本,它并没有一种版本的概念,所以对于更深层的版本细节是无法真正把控的
3、Go Module(类似于java里的maven)
3.1 配置文件(go.mod)
go.mod可以用来配置模块路径、依赖包的版本等
如下例子:
3.2 中间仓库管理依赖库-Proxy
3.3 本地工具- go get和go mod
Go 语言工程实践之测试
在单元测试中,要重视三种度量,代码覆盖率、幂等性、稳定性等