这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记。
1、相关知识
1.1 进程、线程与协程
-
进程:程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位。
-
线程:又称轻量级进程,是进程的一个执行实例,是CPU调度和程序执行的最小单位。内核态,内存消耗是MB级别。
-
协程:在线程中细分出的单位,调度不受操作系统内核所管理,完全由用户控制。用户态,内存消耗是KB级别。
1.2 并发与并行
并发:多线程程序在CPU的一个核上运行 并行:多线程程序在CPU的多个核上运行
2、goroutine
2.1、创建协程
goroutine是 Go 语言并行设计的核心,本质上就是协程。go内部实现了 goroutine 之间的内存共享,比thread更易用、更高效、更轻便。使用极其简单,只需在函数调⽤语句前添加 go 关键字,就可创建并发执⾏单元。
示例:快速打印hello goroutine:0 ~ hello goroutine:4
2.2、CSP
并发程序之间的通信,有通过通信共享内存,和通过共享内存实现通信两种。提倡通过通信共享内存而不是通过共享内存实现通信。
2.3、Channel
go使用make(chan 元素类型, [缓冲大小])来创建通道 通道分为两种:
- 无缓冲通道(同步通道) make(chan int)
- 有缓冲通道 make(chan int, 2)
使用通道示例:A子协程发送0-9数字,B子协程计算输入数字的平方,主协程输出最后的平方数
2.4、并发安全Lock
go 语言的互斥锁:sync.Mutex
示例:5个协程并发对变量执行2000次+1操作
加锁和不加锁的对比结果如下:
2.5、WaitGroup
WaitGroup 对象内部有一个计数器,最初从0开始,它有三个方法Add(), Done(), Wait() 来控制计数器的数量。Add(n) 把计数器设置为n ,Done() 每次把计数器-1 ,wait() 会阻塞代码的运行,直到计数器地值减为0。
注意点:
1、计数器不能为负值
2、WaitGroup 对象不是一个引用类型,通过函数传值的时候需要使用地址,否则程序会进入死锁
3、依赖管理
3.1、GO依赖管理演进
由于不同环境(项目)依赖的版本不同,为了控制依赖库的版本,Go 依赖管理经历了从 GOPATH 到 Go Vendor 到 Go Module 的演进。
3.2、GOPATH
GOPATH 是 go 语言支持的环境变量,GOPATH下会生成以下三个文件夹:
- bin:项目编译的二进制文件
- pkg:项目编译的中间产物,加速编译
- src:项目源码
项目直接依赖src下的代码,go get命令下载的软件包都会在src目录下。
GOPATH的弊端:无法实现package的多版本控制。
3.3、GO Vendor
3.3、GO Module
Go Module是 Go 语言 1.11 版本推出的依赖管理系统,解决了之前的依赖管理存在的问题,实现了定义版本规则和管理项目依赖关系的功能。
- 通过 go.mod 文件管理依赖包版本
- 通过 go get / go mod 指令工具管理依赖包
3.4、本地工具
- go get
- go mod
4、项目实践
4.1、需求描述
4.2、ER图
4.3、分层结构
项目结构整体分为三层:repository数据层,service逻辑层,controoler视图层
- 数据层Repository:关联底层数据模型 Model ,封装外部数据的增删改查。数据层面向逻辑层,对service层透明,屏蔽下游数据差异。
- 逻辑层Service:处理核心业务逻辑,计算打包业务实体Entity ,利用数据层得到封装好的数据再次封装并传到视图层。Service层不关心底层数据的存储形式,只关心核心业务输出。
- 视图层Controller:处理和外部的交互逻辑,以 View 视图的形式返回给客户端。Controller层负责和客户端交互的过程,只关心返回给客户端的数据格式。