这是我参与「第五届青训营 」笔记创作活动的第2天
Go语言进阶
并发编程
-
并发与并行
并发:多线程程序在单核cpu上运行,通过时间片切换实现同时运行的状态。 并行:多线程程序是在多核cpu上运行,通过多核实现多个线程的同时运行。 -
线程与协程
线程:是一种内核态,是系统中一种比较重要的资源。它的各种操作都属于很重的系统操作,比较消耗资源。一个线程可以同时运行多个协程。栈内存大概在MB级别。 协程:是一种用户态,是一种轻量级的线程。它的各种操作较线程而言轻量很多。栈内存大概在KB级别。 -
协程创建
协程使用go关键字进行创建,创建格式为:`go 函数`。函数可以使用普通函数也可以使用匿名函数。 示例如下: //匿名函数形式 go func(paramlist1){ //内容 }(paramlist2) //普通函数形式 go f(paramlist) -
协程通信
通过通信共享内存:使用通道连接协程,是一种特殊机制。 通过共享内存实现通信:使用共享内存进行数据交换,此时需要使用互斥锁,影响通信的性能。 -
Channel(通道)
使用`make`函数进行创建,格式有两种,分别为:无缓冲和有缓冲。channel是并发安全的。 创建示例如下: //无缓冲格式 a := make(chan int) //有缓冲格式 b := make(chan int, 5) -
Lock(锁)
操作共享内存时,为保证并发安全需要使用锁。使用方式较为自由,但是必须有两个步骤,一个是使用Lock函数进行锁定操作,一个是使用Unlock函数进行解锁操作。 示例如下: var ( //共享内存 x int32 //定义锁 lock sync.Mutex ) //锁定 lock.Lock() x++ //解锁 lock.Unlock() -
并发的同步
在Go语言中通过WaitGroup进行并发的同步。本质就是维护了一个计数器,在初始时调用`Add`方法指定一个协程数,在协程完成后调用`Done`方法进行减一操作。在同步的协程中使用`Wait`方法进行等待,直到减为0时,继续执行。 示例如下 //定义WaitGroup var wg sync.WaitGroup //指定协程数 wg.Add(5) for i := 0; i < 5; i++ { go func(j int) { //协程执行完毕,减一 defer wg.Done() println("hello:" + fmt.Sprint(j)) }(i) } //等待 wg.Wait()
依赖管理
-
依赖管理演进
GOPATH -> Go Vendor -> Go Module 目前Go Module广泛使用。 -
GOPATH
`GOPATH`是Go语言的一个环境变量。有三个文件夹,分别为:`bin`,`pkg`,`src`。 bin: 包含项目编译后的二进制文件 pkg: 包含项目编译的中介产物,加速编译 src: 包含项目的源码 项目的所有依赖均存放于`src`中。 弊端:无法实现package的多版本控制。 -
Go Vendor
`Go Vendor`就是在项目目录下增加vendor目录。 vendor: 所有依赖的副本都放在该目录下。 解决了package依赖的冲突问题。 弊端:拉取的第三方包默认是最新版本,无法控制依赖的版本。 -
Go Module
`Go Module`通过`go.mod`文件来管理依赖包版本。通过go get/go mod指令工具管理依赖包。go get自1.17版本开始被弃用,可以使用go install指令。 `Go Module`有三要素,分别为:配置文件、中心仓库、本地工具。 配置文件(go.mod) : 描述依赖,包括版本号、标识符等 中心仓库(Proxy) : 管理依赖,缓存第三方仓库中拉取的依赖,保证依赖的稳定,并减轻第三方压力 本地工具(go get/mod): 对项目依赖进行管理,如下载、更新、删除等
测试
-
单元测试
将测试数据输入到测试单元中得出输出,并与期望进行校对。测试单元包括模块、接口、函数等。 规则: 1. 所有测试文件都以`_test.go`结尾 2. 测试函数命名规范`func TestXxx(*testing.T)` 3. 初始化逻辑放到`TestMain`中 单元测试的写法: 1. 使用testing.T中的方法,如:`t.Errorf(错误信息)` 2. 使用第三方的包,如:使用`github.com/stretchr/testify/assert`中的方法,`assert.Equal(t, 期望输出, 实际输出)` 测试评估方法: 代码覆盖率:测试中执行代码占实际代码的比例。 获取方法:在命令行中使用命令 `go test 包 --cover`或者`go test 源文件 测试文件 --cover` 提升覆盖率方法:对多分支进行测试,以覆盖更多的范围,提高测试的完备性。 -
Mock
使用第三方提供的支持来替换原始的普通函数,让测试环境不依赖于本地的文件,即解除外部依赖,从而保证测试的稳定。 此处使用`github.com/bouk/monkey`中的`Patch(原方法,替换方法)`函数进行替换和`Unpatch(原方法)`函数解除替换。 -
基准测试
用于性能分析 规则: 测试函数命名规范`func BenchmarkXxx(*testing.B)` 写法: 1. 串行测试 for i := 0; i < b.N; i++ { 目标函数 } 2. 并行测试 b.RunParallel(func(pb *testing.PB) { for pb.Next() { 目标函数 } })
Go项目实战
本次项目为,实现发布主题与主题帖子和回显的功能。此处仅介绍开发大体流程:
1. 使用三层架构:即数据层、业务层、视图层
2. 此项目使用goin的web框架
3. 按照业务逻辑进行编码
个人总结
本次学习中,接触到了Go语言独有的并发编程机制:协程。协程具有很多优秀的性质,十分值得学习。
同时还接触到了Go语言的单元测试和基准测试。而对实战的接触也了解了一些Go语言开发项目的结构。
引用
- 字节内部课:Go 语言入门 - 工程实践