Go语言进阶 | 青训营笔记

102 阅读4分钟

Go 进阶

从并发编程的视角了解Go高性能的本质!

高效能的意思:快!

并发与并行

并发:单核多程序,时间片 并行:多核多程序

Go语言可以充分发挥多核优势(多核程序在多个核cpu运行),高效运行。

Goroutine

线程:用户态,轻量级线程,栈MB级别,靠Go调度器调度

协程:内核态,线程跑多个协程,栈KB级别,内核调度 特点:上下文切换快,不经过os的用户态和内核态切换

利用循环核go func进行快速打印,打印结果因其并发异步而随机输出

CSP

通信共享内存:通过通道完成通信共享内存的通讯

image.png

共享内存通信:利用一个共享内存区对各个goroutine进行通讯

image.png

最好用通信共享内存,而不是共享内存通信

channle

1 无缓冲区:make(chan int)

接收和发送端同步 

2 有缓冲区:make(chan int,2)

缓冲区未满,发送不阻塞;
缓冲区未空,接收不阻塞。 

并发安全 Lock

Lock,即sync.Mutex锁 多个协程并发执行一个操作,为了安全需要对该操作互斥执行,但这会损失性能。

waitgroup

并发事务实现同步。

  • Add() 计数器加+delta
  • Done() 计数器-1
  • Wait() 阻塞直到计数器为0
func hello(i int) {
   println("hello world : " + fmt.Sprint(i))
}

func ManyGo() {
   var wg sync.WaitGroup
   for i := 0; i < 5; i++ {
      wg.Add(1)
      go func(j int) {
         defer wg.Done()
         hello(j)
      }(i)
   }
   wg.Wait()
}

使用Wait就不用再主程序中sleep, 因为当计数器为0时,停止阻塞吗,这保证了在主程序退出前,所有协程都能跑完。

总结:

  • 协程:通过高效的调度模型实现高并发操作
  • 通道:通过通信实现共享内存
  • sync相关关键字lock,waitgroup:实现并发安全操作和协程间的同步

依赖管理

背景

  • 实际工程会比一般helloworld更复杂,我们不可能基于标准库0~1编码搭建,而更多的关注业务逻辑的实现。
  • 其他的涉及框架、日志、driver、以及collection等一系列依赖都会通过sdk的方式引入,这样对依赖包的管理就显得尤为重要

Go依赖管理演进

主要讲局限性:

  1. gopath: 不能实现package的多版本控制
  2. govender:无法控制依赖的版本
  3. gomodule:定义了版本规则和依赖关系(选择使用)

依赖管理三要素

  1. 配置文件 go.mod
  2. 中心仓库 goproxy
  3. 本地工具 go get

Go Module实践

go.mod

一般是 [module_path][version]形式 version一般有两种${MAJOR}.${MINOR}.${PATCH}或是vX.Y.Z-yyyymmddhhmmss-comit

依赖分发

GOPROXY是一个 Go Proxy 站点URL列表,可以使用“direct”表示源站。

整体的依赖寻址路径,会优先从proxy1下载依赖,如果proxy1不存在,后下钻proxy2寻找,如果proxy2,中不存在则会回源到源站直接下载依赖,缓存到proxy站点中

使用方法

  1. go get参数 @none 删除, @v1.1.2 下载特定版本
  2. go mod参数 init 初始化, download 下载, tidy 删除不需要的依赖

单元测试

背景

测试一般分为,回归测试一般是QA同学手动通过终端回归一些固定的主流程场景,集成测试是对系统功能维度做测试验证,而单元测试测试开发阶段,开发者对单独的函数、模块做功能验证,层级从上至下,测试成本逐渐减低,而测试覆盖率确逐步上升,所以单元测试的覆盖率一定程度上决定这代码的质量。

  1. 回归测试
  2. 集成测试
  3. 单元测试

单元测试

方法:go test hellowithassert_test.go hello.go

image.png

覆盖率

在实际项目中,一般的要求是50%~60%覆盖率,而对于资金型服务,覆盖率可能要求达到80%;我们做单元测试,测试分支xxx,则要求函数体足够小,这样就比较简单的提升覆盖率,也符合函数设计的单一职责。

规则

  1. 测试文件以_test.go结尾
  2. 函数名称func TestXxxx(t *testing.T)
  3. 初始化逻辑放在TestMain(t *testing.M)

评估测试

代码覆盖率go test加上--cover

mock测试

通过mock实现不对本地文件的强依赖

基准测试

测试cpu损耗之类

函数名称func BenchmarkXxxx(b *testing.b)

项目

稍微感觉有点难度,不过后面能够理解。

总结

  • 学习了Go语言的并发编程,依赖管理,测试相关知识。
  • 学习了Go语言中的goroutine和channel,以及如何使用它们来实现并发编程。
  • 学习了如何使用第三方包管理工具管理项目依赖,
  • 学习如何使用Go语言自带的测试工具进行单元测试。
  • 学习了如何使用gin框架构建一个简单的Web应用程序。

引用

ppt:⁣​‬‬‌​⁤⁤‌⁤​​⁢​⁢⁡​⁡⁤⁣⁤​​​⁤​⁡‍‬⁤⁢‬⁢​⁢​⁢⁣⁡⁢​⁡⁤​‍⁢Go 语言入门 - 工程实践 .pptx - 飞书云文档 (feishu.cn)