Go语言进阶与依赖管理 | 豆包MarsCode AI刷题

72 阅读5分钟

Go语言进阶与依赖管理

一.语言进阶

1. 并发VS并行

(1)并发:多线程程序在一个核的CPU上运行

1.png

(2)并行:多线程程序在多个核的CPU上运行

2.png

Go语言实现了一个并发性能极高的调度模型,通过调度,可以最大限度的利用计算资源,充分发挥多核计算机的优势。

2.协程(Goroutine)

3.png

协程:用户态,轻量级线程,栈KB级别

线程:内核态,线程跑多个协程,栈MB级别

协程的创建和调度由Go语言本身来完成,线程上可以并发的跑多个协程,这就是Go语言适合高并发场景的原因。

可以通过一个go关键字来调用一个协程

3.CSP

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

4.Channel

Channel有make来创建

make(chan 元素类型, [缓冲大小])

​ 无缓冲通道 make(chan int)

​ 有缓冲通道 make(chan int, 2)

无缓冲通道也被称为同步通道,可以通过缓冲区来解决同步问题

4.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)
	}
}

func main() {
	CalSquare()
}

A子协程发送0~9数字

B子协程计算输入数字的平方

主协程输出最后的平方数

题解:

1.创建了一个无缓冲的整数通道src

2.创建了一个缓冲容量为3的整数通道dest

3.A子协程通过一个for循环,将0到9的整数,发送到src通道

4.B子协程通过遍历src通达,将其中的数字平方后,发送到dest通道

5.最后主协程用于将dest通道中的平方结果输出

注解:defer close() 用于确保子协程在退出时,关闭相应的通道。

使用带缓冲的通道可以解决效率问题

二.依赖管理

1.Go依赖管理的3个阶段

  • GOPATH
  • Go Vendor
  • Go Module

目的:

1.不同环境(项目)依赖的版本不同

2.控制依赖库的版本

(1)GOPATH环境变量

bin:项目编译的二进制文件

pkg:项目编译的中间产物,加速编译

src:项目源码

项目代码会直接依赖src下的代码

通过go get 下载最新的包到

弊端:无法实现package的多版本控制

(2) Go Vendor

项目目录下增加vendor文件,用于存放依赖包的副本

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

弊端:

1.无法控制依赖的版本

2.更新项目又可能出现依赖冲突,导致编译出错

(3)Go Module

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

2.通过go mod/go get 指令工具管理依赖包

2.依赖管理的三要素

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

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

3.本地工具 go get / go mod

3.version

(1)语义化版本

major.{major}.{minor}.${patch}

不同的major可以是不兼容的

minor通常是一些新增函数或功能,需要在major下做到前后兼容

patch一般是代码bug的修复

(2)基于commit伪版本

vX.0.0 - yyyymmddhhmmss - abcdefgh1234

"vX.0.0"为版本前缀

"yyyymmddhhmmss"为提交某次commit的时间戳

"abcdefgh1234"为12位的哈希校验码

GOPROXY变量

GOPROXY="proxy1.cn, direct"

服务站点URL列表 direct表示源站

若服务站点不存在依赖,就会到第三方的源站查找相关依赖

go get example.org/pkg@update默认
go get example.org/pkg@none删除依赖
go get example.org/pkg@v1.1.2tag版本,语义版本
go get example.org/pkg@23dfdd5特定的commit
go get example.org/pkg@master分支的最新commit

 

go modinit初始化,创建go.mod文件
go moddownload下载模块到本地缓存
go modtidy增加需要的依赖,删除不需要的依赖

三.测试

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

测试包括回归测试、集成测试、单元测试,覆盖率逐渐变大,成本逐渐降低

1.单元测试

单元测试的规则:

1.所有测试文件以_test.go结尾

2.func TestXxx(*testing.T)

3.初始化逻辑放到TestMain中

一般覆盖率:50%~60%,较高覆盖率80%+

测试分支相互独立、全面覆盖

测试单元粒度足够小,函数单一职责

2.Mock测试:

5.png

3.基准测试:

1.优化代码,需要对当前代码分析

2.内置的测试框架提供了基准测试的能力

四.思考与总结

1. 依赖管理

-   Go语言使用`go.mod`文件来管理依赖。这是从Go 1.11版本引入的特性,它允许开发者声明项目的依赖,并确保依赖的版本一致性。
-   使用`go get`命令可以添加、更新或移除依赖。`go mod tidy`命令可以整理`go.mod`文件,确保依赖的准确性。
-   依赖管理的最佳实践包括定期更新依赖、使用语义化版本控制、避免不必要的依赖以及保持依赖的简洁性。

2. 并发编程

-   Go语言以其原生的并发支持而闻名,通过`goroutine``channel`实现。
-   `goroutine`是轻量级的线程,可以在单个进程中并发执行。`channel`用于在`goroutine`之间通信和同步。
-   使用`sync`包中的工具,如`WaitGroup``Mutex`,可以实现对共享资源的同步访问。

3. 测试与调试

-   Go语言提供了内置的测试框架`testing`,用于编写单元测试和性能测试。
-   编写测试用例是确保代码质量和稳定性的关键。使用`go test`命令可以运行测试。
-   调试工具如`delve`可以帮助开发者在运行时检查和调试代码。

通过不断学习和实践,开发者可以更好地掌握Go语言的高级特性和依赖管理,从而构建高效、可靠的应用程序。