Go 语言基础 - 工程进阶| 青训营笔记

58 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

本节课程主要分为四个方面:

  1. 并发编程
  2. 依赖管理
  3. 单元测试
  4. 项目实战

并发编程

协程Goroutine

Goroutine: 是一种轻量级线程——协程。

1)相对线程,协程进行上下文切换的代价非常的小。
2)由于其上下文切换在用户态下发生,根本不必进入内核态,所以速度很快。而且只有当前goroutine 的 PC, SP等少量信息需要保存。
3)在Go语言中,每一个并发的执行单元为一个goroutine。

使用上也十分容易,相比c++ 多线程,多进程,线程池这些更容易理解也更好操作。只需要在调用函数前增加一个go就开启了一个协程。

在 main 中或者其下调用的代码中才可以使用 go + func() 的方法来启动协程。

main 的地位相当于主线程,当 main 函数执行完成后,这个线程也就终结了,意味着其开辟的协程无论是否终结都会自动终止。

如果想要执行,则需要对主线程休眠,确保协程能够完成返回。

通道Channel

可以认为一个管道连接多个go routine程序进行数据传递。 这种通信会让我们想起共享内存 但是这两者有着很大的不同。

共享内存是指多个线程或进程在内存中传递消息。而通道更像一个队列,遵循先入先出这样的规则。而且通道只能传递一种数据类型的数据。

channel 又分为无缓冲通道和有缓冲通道。二者的区别在于开辟的空间大小。

pipeline := make(chan int)

pipeline := make(chan int,3)

下面就是一个计算平方数的例子,很好的表明channel的使用方法以及它是如何传递的。

线程同步WaitGroup

之前提到过主线程完成后会终止内部所有协程的运行,为了保证协程能够运行结束我们增加了sleep来确保协程能够运行结束。但在实际开发中,开发人员是无法预知所有的 goroutine 需要多长的时间才能执行完毕,sleep 多了主程序就阻塞了, sleep 少了有的子协程的任务就没法完成。

WaitGroup 能够很好解决这一问题

var 实例名 sync.WaitGroup 

实例化完成后,就可以使用它的几个方法:

  • Add:初始值为0,你传入的值会往计数器上加,这里直接传入你子协程的数量
  • Done:当某个子协程完成后,可调用此方法,会从计数器上减一,通常可以使用 defer 来调用。
  • Wait:阻塞当前协程,直到实例里的计数器归零。

依赖管理

Gopath

项目代码直接依赖src下的源码,利用 go get 下载最新的包到src目录下

缺点:无法满足多版本控制,只能保存一个版本的代码

Go Vendor

在目录增加了vector 文件 保存每个版本的依赖副本

缺点:无法解决同一个包不同版本的问题,可能存在不兼容问题导致依赖冲突

Go Module : go.dev/blog/using-…

通过go.mod 文件管理依赖包的版本

能够保存版本信息,依赖关系,兼容关系

go proxy

作为一个缓存站点,对软件内容进行缓存,确保go module 每次访问都能获取

go proxy 是一个url列表 可以指定源站进行寻找依赖

单元测试

单元测试概念和规则:示例

  • 所有的测试文件以_test.go 结尾
  • func TestXxx(*testing.T)
  • 初始化逻辑放在TestMain中
  • 可使用 go test 运行

Mock测试:github.com/bouk/monkey

单元测试需要保证稳定性和等幂性,mock机制能够很好的保障这一点

以monkey 库为例,monkey patch 的作用域在runtime。能够将内存中函数地址替换为运行时的函数地址,将待打桩函数或者方法进行跳转

基准测试:pkg.go.dev/testing#hdr…

基准测试可以反复的运行函数,从而建立基准,并且无须执行运行次数,因为框架会通过调整次数来获得可靠的数据集,基准测试结束后将获得一个报告,包含了运行次数以及运行一次消耗的时间,单位为 ns。

基准测试函数的命名方式为 BenchmarkXxx 并且要求传入一个 *testing.B 类型。