Go语言进阶day2|青训营笔记

161 阅读2分钟

工程实践角度

1.语言进阶--Go并发编程

1.基于Go并发编程相关概念

图片.png

(引用自课程PPT)

Go语言开发快的原因 1.Go可以充分发挥多核优势,高效运行。Go语言就是为并发而生的。

1.1Goroutine

协程与线程 在go语言中,协程被认为是轻量级的线程, 和线程不同的是,操作系统内核 感知不到协程的存在, 协程的管理依赖于Go语言运行时自身提供的调度器 同时Go语言中的协程是从属于某一个线程的。

线程处于内核态,栈MB级别,线程跑多个协程; 协程处于用户态,是轻量级别的线程,栈KB级别。

package main
import (
   "fmt"
   "time"
)
func hello(i int) {
   println("hello goroutine:" + fmt.Sprint(i))
}
func main() {
   for i := 0; i < 5; i++ {
      go func(j int) {
         hello(j)
      }(i)
   }
   time.Sleep(time.Second)
}
//乱序输出说明是并行
//hello goroutine:4
//hello goroutine:3
//hello goroutine:2
//hello goroutine:1
//hello goroutine:0

1.2CSP协程之间的通信

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

通过通信共享内存使用通道,通过内存实现通信使用互斥量,而互斥量一定程度上会影响性能,因此提倡通信共享内存。

1.3Channel

定义channnel的方法:make(chan 元素类型[缓冲大小])

//无缓冲通道 
//可以实现发送的goroutine和接收的goroutine同步,也被称为同步通道
make(chan int)  
//有缓冲通道
make(chan int ,2)

例子

package main
func main() {
   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)
   }
} //0 1 4 9 6 25 36 49 64 81

1.4 并发安全Lock(sync包下关键字实现并发安全)

协程的执行顺序不确定会导致并发安全问题。多个协程执行同一个方法,且多个线程存在共享变量,由于这些协程执行顺序是不确定的,由这些协程同步修改使用一些变量时,产生了不确定的结果。

可以使用锁实现并发安全。

1.5 WaitGroup(sync包下关键字实现协程同步)

go中使用WaitGroup实现并发任务的同步。内部维护了一个计数器。 图片.png

(引用自课程PPT)

``` package main import ( "fmt" "sync" ) func hello(i int) { println("hello goroutine:" + fmt.Sprint(i)) } func main() { var wg sync.WaitGroup wg.Add(5) for i := 0; i < 5; i++ { go func(j int) { defer wg.Done() hello(j) }(i) } wg.Wait() } //hello goroutine:4 //hello goroutine:2 //hello goroutine:3 //hello goroutine:1 //hello goroutine:0 ```

2.依赖管理演进

实际项目中我们要将更多的精力去关注业务逻辑,而其他的依赖可以通过sdk的方式引入,因此对依赖包的管理就十分重要。

2.1 Go的依赖管理

GOPATH->Go Vender->Go Module

2.1.1 GOPATH

GOPATH是GO语言支持的环境变量,是GO项目的工作区。 图片.png 问题:无法实现package的多版本控制

2.1.2Go Vender

项目目录下增加vender文件,所有依赖包副本形式放在vender目录下。依赖寻址方式:vender->GOPATH

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

2.1.3 Go Module

通过 go.mod 文件管理依赖包版本; 通过 go get/go mod 指令工具管理依赖包

2.2 依赖管理三要素

  1. 配置文件,描述依赖 go.mod
  2. 中心仓库管理依赖库 Proxy
  3. 本地工具 go get/mod

与Java中的maven类似。

2.2.1依赖配置-go.mod

依赖中标识了版本号 图片.png

2.2.2 依赖配置-version

//语义化版本
${SIMAJOR}.${SIMINOR}.${SPATCH}
${大版本}.${新增函数}.${代码修复}
//V1.3.0V2.3.0
//基于 commit 伪版本
vx.0.0-yyyymmddhhmmss-abcdefgh1234
//版本前缀-时间戳-哈希前缀
v0.0.0-20220401081311-c38fb59326b7
v1.0.0-20201130134442-10cb98267c6c

2.2.3 依赖配置- indirect

间接依赖标识

2.2.4 依赖配置- incompatible

  • 主版本2+模块会在模块路径增加/vN
  • 后缀对于没有go.mod文件并且主版本2+的依赖,会加incompatible(标识出来可能会出现一些不兼容的代码逻辑)

2.2.5 依赖配置-依赖图

选择构建的最低版本

2.2.6 依赖分发-变量 GOPROXY

Proxy能够保证依赖的稳定和可靠性。

2.2.7 工具-go get

go get example.org/pkg:

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

- 2.2.8 工具-go mod

go mod

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

3.测试

测试分为:回归测试、集成测试、单元测试。覆盖率逐渐变大,成本逐渐减低。