这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
1.并发控制
1.1 并发VS并行
并发:多线程程序在一个CPU上运行
并行:多线程程序在多核CPU上运行
Go语言可以发挥多核优势,高效运行
协程 协程 协程 协程 用户态
线程 线程 内核态
协程:用户态,轻量级线程,栈MB级别。
线程:内核态,线程跑多个协程,栈KB级别。
1.2 go的并发编程
Goroutine(协程)
在一个在一个go程序里面,main函数对应了主线程,而在主线程里面开一个轻量级的线程就被叫做协程。
使用go关键字可以快速开启一个协程。
例如:
func hello(i int){
println("hello goroutine:"+fmt.Sprint(i))
}
func HelloGoRoutine() {
for i := 0; i < 5; i++ {
go func(j int) {
hello(j)
}(i)
}
time.Sleep(time.Second)
}
Channel(通道)
协程之间用来通信和传输数据的工具叫通道,分为有缓存通道和无缓冲通道。顾名思义,缓冲通道可以提供一片空间暂时存放数据,不过当缓冲满后需要释放缓冲才可继续传输数据。通道的简单定义如下:
//make(chan 元素类型,缓冲大小)
make(chan int,2)//有缓冲通道
make(chan int)//无缓冲通道
Lock(锁)
并发编程必然涉及到一个并发安全的问题,多个不同的线程可能会对同一块内存空间进行操作造成内存泄漏等很多安全问题,这时候就需要一个锁来保证内存空间的安全。
var lock sync.Mutex//定义一个锁
lock.Lock()//上锁
lock.UnLock()//解锁
WaitGroup
在Goroutine那里的样例里面协程的阻塞是使用延时来进行的,但在实际情况中我们并不能具体的知道那个准确合理的时间,这时候就要引入WaitGroup来解决那个问题了。
//定义一个WaitGroup
var wg sync.WaitGroup
wg.Add(5)//计数器+5
wg.Done()//计数器-1
wg.Wait()//阻塞直到计数器为0
2.依赖管理
依赖管理的重要性:开发和运行需要依赖包才能实现。
go的依赖管理有三个阶段,分别是gopath、govendor、gomodule。
gopath
GOPATH是我们go的环境变量之一,为go工作目录的路径,高版本会自动配置GOPATH。go的工作目录下有三个子目录分别是bin、pkg、src。
- bin:存放项目编译的二进制文件。
- pkg:存放项目编译的中间产物。
- src:存放项目源码。 项目代码直接依赖src下的代码,go get命令可下载最新的依赖包到src下,不过gopath的弊端是无法进行依赖包的多版本控制。
gomodule
在前两个阶段都存在极大弊端的情况下,gomodule阶段诞生了。
- 通过go.mod文件管理依赖包版本。
- 通过go get/go mod 指令管理依赖包。
- 终极目标:定义版本规则和管理项目依赖。
- 依赖管理三要素:go.mod、Proxy、go get/mod
go.mod
配置文件,描述依赖。
在1.6版本之后,创建项目会自动生成一个go.mod文件(类似java项目的pom.xml文件),还会引入一个go.sum文件(记录依赖的hash值,防止依赖被人修改,详情可参考(5分钟玩转go.sum - 掘金 (juejin.cn))。
在依赖冲突的情况下,go会默认选择最低的兼容版本,举个例子:A依赖C-1.2,B依赖C-1.3,C-1.4和C-1.5兼容了C-1.2和C-1.3的功能,那么go会默认选择C-1.4的版本。
Proxy(依赖分发)
GOPROXY=https://goproxy.cn,direct
上面的即go拉取依赖的服务站点地址配置,按从左往右的优先级去查找,直到找不到会查找下一个,最后是direct(默认的源站)。一般来说建议配置一个国内镜像这样在下载依赖的时候速度会快很多。当依赖被拉取下来后会在本地进行管理。注意:如果项目有导包,一定要将依赖拉取到本地保证本地有这个依赖,否则直接在导包的位置导入一个包的URL地址也是会爆红的。
- Proxy保证了依赖的稳定性:本地依赖库,无需担心依赖作者、修改依赖版本导致项目不可用。
- Proxy保证了依赖可用性:即使依赖作者删除依赖,本地依赖依然可以保证项目的开发和运行。
参考
- 初识go语言-go语言工程进阶 | 青训营笔记(juejin.cn/post/719035…
- 字节跳动青训营Go语言进阶——工程进阶课程