01.语言进阶
从并发编程的视角带大家了解GO高性能的本质
01.并发 VS 并行
并发:时间片切换实现(单核)
并行:多核cpu上运行(go充分发挥多核优势)
1.1 Goroutine 协程
go实现高并发的手段
通过对比线程来理解协程
线程为内核态,创捷切换停止属于很重的系统操作
协程则为用户态,轻量级,创建和调度由GO语言本身完成
GO语言一次可创建上万的协程,GO语言更适合高并发的原因所在 协程的开启,在函数前加上一个GO关键字
eg:
func hello(i int) {
fmt.Println("hello goroutine :" + fmt.Sprint(i))
}
func main() {
for i := 0; i < 5; i++ {
go func(j int) { //go 关键字开启协程
hello(j)
}(i)
}
time.Sleep(time.Second) //用sleep完成阻塞,保证子线程在完成前,主线程不退出
}
输出结果(乱序是通过并行的打印输出):
1.2 CSP(Communicating Sequenential Processes)通讯顺序过程
go 提倡通过通信共享内存(下图左), 而不是通过共享内存实现通信(下图右)
一个协程与另一个协程见通信
通过通道:先入先出通过Channel(见1.3)
通过临界区:通过加锁(见1.4)完成临界区访问问题
1.3Channel 渠道
创建通过make关键字
make(chan 元素类型,[缓冲大小])
- 无缓存通道 make(chan int)
- 有缓存通道 make(chan int,2)
无缓存通道又本成为同步通道,解决同步问题的方式就是使用有缓冲通道(典型的生成消费模型·操作系统知识点)
func CalSquare() {
src := make(chan int) //A,B协程的通信
dest := make(chan int, 3) //生成消费不均衡
//生产者 子协程A发送0~9数字
go func() {
//关闭
defer close(src)
for i := 0; i < 10; i++ {
src <- i
}
}()
//子协程B计算输入数字的平方
go func() {
defer close(dest)
for i := range src {
dest <- i * i
}
}()
//消费者 主协程输出最后的平方数
for i := range dest {
//复杂操作
println(i)
}
}
顺序打印,安全性验证
1.4并发安全Lock
go也提供通过临界区通信
不加锁有一定概率产生错误结果,开发中避免对共享内存进行非并发安全的读写操作
1.5 WaitGroup
以上代码用sleep阻塞,这里我们介绍WaitGroup实现并发任务同步
计数器为0则所有任务完成
对1.1代码优化(快速打印)
func ManyGoWait() {
var wg sync.WaitGroup
wg.Add(5) //五个hello,所以数量设置五
for i := 0; i < 5; i++ {
go func(j int) {
defer wg.Done() //执行完成后-1
hello(j)
}(i)
}
wg.Wait() //主协程阻塞直到计数器为0的时候并发执行
//time.Sleep(time.Second)
}
快速打印结果:
1.5 小结
- Goroutine 协程的高效调度
- Channel 通过通信实现共享内存
- Sync 包下关键字lock,GoWait,实现并发安全操作和携程间同步
02.依赖管理
了解GO语言依赖管理的演进路线
学会站在巨人的肩膀上
用经过验证的组件工具,来提升自己的研发效率
- 工程项目不可能基于标准库0~1编码搭建
- 学会使用管理依赖库,更多精力放在项目上
2.1 Go 依赖管理演进
- 不同环境(项目)依赖的版本不同
- 控制依赖库的版本
2.1.1 GOPATH
GO的工作区
| filename | 用途 |
|---|---|
| bin | 项目编译的二进制文件 |
| pkg | 项目编译的中间产物,加速编译 |
| src | 项目源码 |
项目代码直接依赖src下的代码
go get 下载最新版本的包到src目录下
GOPATH——弊端
无法实现package多版本控制
V2可能把A函数删掉了,解决方法增加Go Vendor
2.1.2 Vendor
通过每个项目引入一份依赖的副本vendor
GO Vendor——弊端
无法清晰标识依赖版本的概论
解决方法引入 Go Module
2.1.2 Module
依赖配置——go.mod
唯一定位每个版本的每次提交
依赖配置——version
版本规则一:语义化版本
MAJOR:大版本,不同major见可不兼容,代码隔离
MINOR:新增函数功能,需要前后兼容
PATCH:bug修复
版本规则二:基于 commit 伪版本
版本前缀,同语义化+时间戳+12位哈希校验码
依赖配置——indirect
用indirect标识非直接依赖
依赖配置——incompatible
标识可能会存在不兼容代码逻辑
依赖配置——依赖图
选择满足本次构建的最低版本
依赖分发——回源
依赖分发——Procy
服务站点,缓存原站中的内容,保持依赖的稳定性
依赖分发——变量GOPROXY
前面两个站点都没有依赖的话,会回到第三方代码平台上
项目查找依赖路径
工具 go get 和 go mod
运行前可以先tidy一下,减少构建时间
2.1.3小结
03.测试
从单元测试实践出发,提升大家的质量意识
3.1单元测试
3.1.1 单元测试——规则
04.项目实战
通过项目需求、需求拆解、逻辑设计、代码实现带大家感受下真实的项目开发
4.1.1 需求设计
需求背景:社区话题页码
4.1.2 需求用例
实体:话题+帖子
抽象出topic和postlist结构体
4.1.3 ER图——Entity Relationship Diagram
一对多关系
4.1.4 分层结构
- 数据层:数据Model,外部数据的增删改查
- 逻辑层:业务Entity,处理核心业务逻辑输出
- 视图层:视图view,处理和外部的交互逻辑
4.1.5 组件工具
- gin 高性能 go web 框架
- Go Mod go mod init
go get gopkg.in/gin-gonic/gin.v1@v1.3.0
如果显示失败在终端执行:
go env -w GOSUMDB=off
go env -w GO111MODULE=on
go env -w GOPROXY=goproxy.cn,direct
再尝试go get gopkg.in/gin-gonic/gin.v1@v1.3.0