[ Go语言工程实现 | 青训营笔记 ]

75 阅读3分钟

一、Go语言进阶和依赖管理

01、语言进阶

1.1 Goroutine

并发编程

多线程程序在一个核的CPU上运行

Go可以充分发挥多核优势,高效运行

协程

用户态,轻量级线程

package main

import "fmt"

func hello(i int) {
	println("hello goroutine: " + fmt.Sprint(i))
}

func HelloGoRoutine() {
	for i := 0; i < 5; i++ {
		go func(j int) {
			hello(j)
		}(i)
	}
}

1.2 CSP(Communicating Sequential Processes)

9.png

提倡通过通信共享内存而不是通过共享内存而实现通信

1.3 Channel

通过make关键字创建

//make(chan 元素类型,[缓冲大小])
make(chan int)  //有缓冲通道
make(chan int,2) //无缓冲通道

10.png

func CalSquare() {
	src := make(chan int)
	dest := make(chan int, 3)
	go func() {
		defer close(src)
		for i := 0; i < 10; i++ {
			src <- i
		}
	}()

	go func() {
		defer close(dest)
		for i := range src {
			dest <- i * i
		}
	}()

	for i := range dest {
		println(i)
	}
}

1.4 并发安全 Lock

  • 当多个goroutine同时访问和操作共享的数据时,由于操作的顺序不确定,可能导致意外的结果。例如,两个goroutine同时对同一个变量进行写操作,结果可能出现数据损坏或不一致的情况。

  • 在多个goroutine并发访问共享资源时,为了保证数据的一致性,通常需要使用互斥锁来进行同步。然而,互斥锁的错误使用可能导致死锁或活锁的情况,从而使程序无法继续执行。

1.5 WaitGroup

在Go语言中,WaitGroup是一种用于等待一组goroutine完成执行的同步机制。WaitGroup可以用于确保在所有的goroutine执行完成之后再继续执行后续的操作。

func ManyGoWait() {
    var wg sync.WaitGroup
    //使用Add方法增加计数器的值
    wg.Add(5)
    for i := 0; i < 5; i++ {
        go func(j int) {
            //调用Done方法减少计数器的值
            defer wg.Done()
            hello(j)
        } (i)
    }
    //通过调用wait方法进行阻塞,直到计数器归零
    wg.Wait()
}

02、依赖管理

GOPATH -> Go Vendor -> Go Module

2.1.1 GOPATH

  • 所有项目代码直接依赖src下的代码
  • go get下载最新版本的包到src目录下

弊端

在A、B项目同时依赖于某一package的不同版本时出现问题,无法实现package的多版本控制

2.1.2 Go Vendor

  • 增加vendor文件,所有依赖包副本形式放在此处
  • 依赖寻址方式:vendor => GOPATH

通过每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题

弊端

  • 无法控制依赖的版本
  • 更新项目又可能出现依赖冲突,导致编译出错

2.1.3 Go Module

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

2.2 依赖管理的三要素

1、配置文件,描述依赖 go.mod

2、中心仓库管理依赖库 Proxy

3、本地工具 go get/mod

2.3.1 依赖配置-go.mod

11.png

2.3.2 依赖配置-version

  • 语义化版本
  • 基于commit伪版本

2.3.3 依赖配置-indirect

a->b->c

ab之间直接依赖,ac直接间接依赖,对于不是直接依赖的依赖用indirect标识出来

2.3.4 依赖配置-依赖图

选择最低的兼容版本

2.3.5 依赖分发-回源

12.png

  • 无法保证构建稳定性
  • 无法保证依赖可用性
  • 增加第三方压力

2.3.6 依赖分发-Proxy

13.png

2.3.7 工具-go get

14.png

2.3.8 工具-go mod

15.png

二、Go语言工程实践之测试

03、测试

测试是避免事故的最后一道屏障

16.png

3.1 单元测试

17.png

  • 所有测试以_test.go 结尾
  • func TestXxx(*testing.T)
  • 初始化逻辑放到TestMain中

代码覆盖率

代码覆盖率是一种衡量测试覆盖程度的度量标准,用于确定在单元测试中有多少代码被执行过。它衡量了测试套件执行期间代码的执行路径,并告诉你你的测试是否覆盖了应用程序中的每个语句、分支、条件和边界情况。

在Go语言中,可以使用内置的测试工具go test来计算代码的覆盖率。

3.2 单元测试-依赖

幂等、稳定

3.3 单元测试-文件处理

3.4 单元测试-Mock

在编写单元测试时,有时我们需要模拟(Mock)一些依赖项或外部资源的行为,以便更好地控制测试环境并隔离被测试单元的逻辑。使用Mock可以帮助我们创建虚拟的对象,模拟预期的行为,并对被测试单元进行准确的验证。

3.5 基准测试

基准测试(Benchmarking)是一种评估程序性能的方法,用于确定代码在特定条件下的执行速度。在Go语言中,我们可以使用内置的基准测试框架来编写和运行基准测试。

基准测试的目的是衡量一段代码在给定输入下的执行时间。它对于性能优化和代码优化非常有用,可以帮助我们找到程序中的性能瓶颈和低效的部分。