1.Go语言中的并发安全和锁
如果没有锁: 在我们的项目中,可能会存在多个goroutine同时操作一个资源(临界区),这种情况会发生竞态问题(数据竞态)。和操作系统感觉很像
互斥锁:互斥锁能够保证同时只有一个goroutine可以访问共享资源,是一种常用的控制共享资源访问的方法。Go语言中使用sync包的Mutex类型来实现互斥锁。使用互斥锁能够保证同一时间有且只有一个goroutine进入临界区,其他的goroutine则在等待锁;当互斥锁释放后,等待的goroutine才可以获取锁进入临界区,多个goroutine同时等待一个锁时,唤醒的策略是随机的。但是,这种方式还是有问题的,读写都会等待,大大降低了程序效率。
读写互斥锁
在大多数场景下,是读多写少的,当我们并发的去读取一个资源不涉及资源修改的时候是没有必要加锁的。这种情况就可以使用读写互斥锁,Go语言中使用sync包中的RWMutex类型。 读写锁分为两种:读锁和写锁。当一个goroutine获取读锁之后,其他的goroutine如果是获取读锁会继续获得锁,如果是获取写锁就会等待;当一个goroutine获取写锁之后,其他的goroutine无论是获取读锁还是写锁都会等待
- WaitGroup
sync.WaitGroup 是 Golang 中常用的并发措施,我们可以用它来等待一批 Goroutine 结束。
WaitGroup 的源码也非常简短,抛去注释外也就 100 行左右的代码。但即使是这 100 行代码,里面也有着关乎内存优化、并发安全考虑等各种性能优化手段。
WaitGroup 的用法非常简单:使用 Add 添加需要等待的个数,使用 Done 来通知 WaitGroup 任务已完成,使用 Wait 来等待所有 goroutine 结束。
依赖管理
实际工程会相对复杂,我们不可能基于标准库0~1编码搭建,而更多关注业务逻辑地实现,而其他涉及框架、日志、等一系列依赖都会通过sdk的方式来引入,这样对依赖包的管理就显得尤为重要。
WaitGroup 的源码也非常简短,抛去注释外也就 100 行左右的代码。但即使是这 100 行代码,里面也有着关乎内存优化、并发安全考虑等各种性能优化手段。
go语言低版本使用 path 和 vender 管理第三方依赖,现在为go.mod
如上图,一组package的集合即为一个module,一个module下的package版本号相同。一个项目包含一个或者多个module,每个module包含一个或多个 package,每个package包含一个或多个源文件。
它解决了2个重要的问题:
- 准确记录项目依赖:当前项目使用了哪些第三方包、依赖包的版本
- 可重复构建:当前项目在任何环境或平台构建产物相同
GOPRATH:无法实现package多版本控制
GOVENDER:无法控制依赖版本