go语言进阶与依赖管理
1.并发编程
GO语言是并发编程优势是快Go可以充分发挥多核优势,高效运行
1.1Goroutine
协程是用户态,轻量级线程,栈是KB级别的,运行时单线程的
线程是内核态,线程跑多个协程,栈是Mb级别的
线程适合需要密集IO操作或CPU密集型任务,而协程则适合IO密集型任务或需要高并发支持的场景。
Go语言可以开多个协程来提高运行速度,在Go中开启协程的时候只需要在调用函数的前面加上go关键字就行了
例如:
func fp(i int){
println("hello:"+fmt.Sprint(i))
}
func xc(){
for i:=0;i<5;i++{
go fp(i)
}
time.Sleep(time.Second)
}
func main() {
xc()
}
执行结果是随机的,因为这个主线程开了5个子协程,而且协程之间是并发的所以结果是随机的,而且time.Sleep(time.Second)这个语句必须加上因为如果不加上,xc函数的线程开启5个协程之后就结束了并不会等待5个协程开始和结束所以必须加上这句话来让主线程等待协程运行完毕
1.2
CSP(Communicating Sequential Processes)并发编程模型
提倡通过同心共享内存而不是通过共享内存实现通信
goroutine 是轻量级线程,它们是 Go 中并发编程的基本单位,每个 goroutine 运行在单独的栈上,并且通过 Go runtime 调度器来管理它们的执行。和线程相比,goroutine 的创建、调度、销毁开销较小,因此可以支持大量的并发执行。
channel 是 goroutine 之间通信的通道,它可以看做一个类型为 chan 的数据结构,提供了两个方法,send 和 receive。使用 channel 可以达到多个 goroutine 并发访问共享资源但不用加锁同步的目的。
通过 goroutine 和 channel,Go语言中的并发编程变得异常简单,我们可以在 goroutine 中并发执行操作,然后通过 channel 实现不同 goroutine 间的通信。这种并发处理方式下,不同 goroutine 间通过直接调用传递消息进行通信,而不需要共享或访问共享资源,这种方式避免了传统并发编程中繁琐的锁定、解锁和同步操作,简化了代码的编写。
1.3channel通道
可以用make关键字具体是make(chan 元素类型,[缓冲大小])
make(chan int)无缓冲通道(同步通道)
make(chan int,2)有缓冲通道
示例代码
func cs(){
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)
}
}
在这段代码中,go func() 开启了一个在 goroutine 中执行的函数,并开启了一个用于发送数据的通道 src。在 go func() 中通过发送操作向通道 src 中发送了一些数据,这些数据将会被后面的 goroutine 用于处理并发送到另一个通道 dest 中。因此,当所有数据被处理完成后,我们需要关闭 src 这个通道,以便通知后面的 goroutine 停止处理数据并退出。
其中的defer是延迟实现某个函数,当 defer 关键字后面跟着的函数调用完成后,Go 语言会自动将其压入到一个“defer 栈”中。当当前函数执行完毕并且立即返回时,Go 语言就会按照“后进先出”的顺序执行这些被推迟的函数调用。close 函数是用于关闭通道的一个内置函数避免资源泄露或者死锁
其中的dest函数设置了缓冲区避免后续的复杂操作影响dest的执行效率
1.4并发安全Lock
在 Go 语言中,合理的使用锁可以保证并发程序的正确性和性能,避免数据静态化
1.5waitgroup
在go语言中,waitgroup来实现写成之间的同步,避免主协程过早或过晚关闭导致输出错误
Add(delta int)计数器+delta
Done()计数器减一
wait()阻塞直到计数器为0
计数器:开启协程加1;执行结束-1;主协程阻塞直到计数器为0
2.依赖管理
2.1Go依赖管理演进
从GOPATH到GO Vender到现在的Go Module
(1不同环境(项目)依赖的版本不同;(2控制依赖库的版本
2.1.1
环境变量GOPATH,项目代码直接以来src下的代码 go get 下载最新版本的包到src目录下
弊端:A和B依赖于某一package的不同版本;问题:无法实现package的多版本控制
2.1.2 Go Vendor
项目目录下增加vender文件,所有依赖包都以副本的形式放在vendor下;解决了package的多版本
弊端:无法控制依赖的版本,更新项目又可能出现依赖冲突,导致编译错误
2.1.3Go module
通过go.mod文件管理依赖包版本
通过 go get/ go mod 指令工具管理依赖包
2.2依赖管理三要素
1.配置文件,描述依赖 go.mod
2.中心仓库管理依赖库 Proxy
3.本地工具 go get/mod Go语言工程实践之测试
测试有回归测试,集成测试,单元测试从左到右,覆盖率逐渐变大但是成本逐渐降低
3.1单元测试
包括输入,测试单元(函数,模块等)输出最后与期望校对
3.1.1测试规则
1)所有测试文件都是以_test.go结尾
2)func TestXxx(*testing.T)
3)初始化逻辑放到TestMain中
一般测试覆盖率50%到60%左右
测试分支相互独立、全面覆盖
测试单元粒度要足够小,函数单一职责
3.2单元测试-依赖
外部依赖大于等于稳定&幂等
3.4单元测试-mock
快速mock函数:为一个函数打桩;为一个方法打桩
3.5基准测试
优化代码,需要对当前代码分析
内置测试框架提供了基准测试的能力