Go语言工程实践进阶-并发安全与依赖管理 | 青训营笔记

53 阅读1分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 10 天

并发安全锁

package main

import (
	"sync"
	"time"
)

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 addWithoutLock()
	}
	time.Sleep(time.Second)
	println("WithoutLock:", x)
	x = 0
	for i := 0; i < 5; i++ {
		go addWithLock()
	}
	time.Sleep(time.Second)
	println("WithLock:", x)
}

func main() {
	Add()
}

image.png

可以发现使用锁和不使用锁得到结果不同,不使用锁得到的结果不是我们预期想要的。.

依赖管理

对于hello world以及类似的单体函数只需要依赖原生SDK,而实际工程会相对复杂,我们不可能仅仅依赖标准库的代码去搭建。

实际的工程中我们更多关注业务逻辑的实现,而其他的一系列依赖都会通过sdk的方式引入,这样就对依赖包的管理就十分重要。

GOPATH 模式

从 Go 1.8 版本开始,安装 Go 开发包时会默认为 GOPATH 变量设置一个目录路径,它表示的是 Go 语言的工作目录,这个目录下会有三个子目录,它们分别是:

  • bin:存放编译后生成的二进制可执行文件
  • pkg:存放编译后生成的 .a文件
  • src:存放项目的源代码,有自己写的代码,还有通过 go get命令下载的包

GoPath 模式的弊端

在 Go 1.11版本之前,开发者是必须要配置 这个GOPATH环境变量的,这种代码代码管理模式存在比较严重的问题就是没有版本控制。

因为多个项目都会放在src目录下,而每个项目依赖的一些第三方包也是下载在src目录下的,当升级某个依赖包时就是全局升级,引用这个依赖包的项目都跟着升级包的版本了,这样是一件很危险的事,因为你不知道升级的包在另外一个项目中是否能正常运行。而且当多人协同开发时,你不知道别人下载的包是不是你所用的那个版本,容易出错且不好排查原因。

GO Vendor模式

在每个项目下都创建一个 vendor 目录,每个项目所需的依赖都只会下载到自己vendor目录下,项目之间的依赖包就互不影响了。在使用包时,会先从当前项目下的 vendor 目录查找,然后依次向上级目录查找。这种方式依旧是 GOPATH 模式下的,它解决了不同项目不能使用不同版本库的问题。

GO Vendor模式的弊端

  • 无法控制依赖的版本
  • 更新项目有可能出现依赖冲突,导致编译出错

Go Module模式

GoModules 模式是 Go 语言 1.11 版本正式推出的,在 1.14 版本时,官方就发话 GoModules 的模拟已经成熟,可以用于生产环境,在1.16版本后默认开启的模式。

通过go.mod文件管理依赖的版本

通过go get/go mod指令工具管理依赖包

工程不用全放在go path/src目录下

特点:

GO MODULE模式下所有依赖的包存放在$GOPATH/pkg/mod目录下

项目中需要有go.mod文件,来应用$GOPATH/pkg/mod

依赖管理三要素

  1. 配置文件 描述依赖 go.mod
  2. 中心仓库管理依赖库 Proxy
  3. 本地工具 go get/mod