Go 语言进阶与依赖管理 | 青训营

73 阅读2分钟

1. 语言进阶

1.1 并发 VS 并行

多线程程序在一个核的cpu上运行 image.png

多线程程序在多个核的cpu上运行 image.png

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

1.2 Coroutine

image.png

协程: 用户态,轻量级线程,栈 KB 级别。 线程: 内核态,线程跑多个协程,栈 MB 级别

快速打印 hello goroutinue 0-4

package main

import (
	"fmt"
	"time"
)

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)
	}
	time.Sleep(time.Second)
}

func main() {
	HelloGoRoutine()
}

image.png

go 语言可以使用 go 关键字来快速实现协程

可以看到输出并不是有序的,说明协程是并行的

1.2 CSP(Communicating Sequential Processes)

image.png

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

1.3 Channel

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

image.png

  • A 协程发送 0~9 个数字
  • B 协程计算输入数字的平方
  • 主协程输出最后的平方数
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)
	}
}

image.png

image.png

1.4 并发安全 Lock

对变量执行 2000 次 +1 操作,5 个协程并发执行

var (
	x    int64
	lock sync.Mutex
)

func addWithLock() {
	for i := 0; i < 2000; i++ {
		lock.Lock()
		x += 1
		lock.Unlock()
	}
}

func addWithoutLock() {
	for i := 0; i < 2000; i++ {
		x += 1
	}
}

func Add() {
	x = 0
	for i := 0; i < 5; i++ {
		go addWithoutLock()
	}
	time.Sleep(time.Second)
	println("WithoutLock:", x)
	x = 0
	for i := 0; i < 5; i++ {
		go addWithLock()
	}
	time.Sleep(time.Second)
	println("WithLock:", x)
}

image.png

1.5 WaitGroup

image.png

计数器: 开启协程+1,执行结束-1,主协程阻塞直到计数器为0

修改之前的打印 hello goroutine 程序

func HelloGoRoutine() {
	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()
	//time.Sleep(time.Second)
}

image.png

2. 依赖管理

2.1 Go 依赖管理演进

image.png

  • 不同环境(项目)依赖的版本不同
  • 控制依赖库的版本

2.1.1 GOPATH

image.png

  • 环境变量$GOPATH
  • 项目代码直接依赖 src 下的代码
  • go get 下载最新版本的包导 src 目录下

GOPATH 弊端

image.png

场景 : A 和 B 依赖于某一 package 的不同版本 问题 : 无法实现 package 的多版本控制

2.1.2 Go Vendor

  • 目录下新增 vendor 文件,所有依赖包副本形式放在 $ProjectRoot/vendor
  • 依赖寻址方式:vendor -> GOPATH

image.png

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

Go Vendor 弊端

image.png

问题

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

2.1.3 Go Module

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

终极目标: 定义版本规则和管理项目依赖关系

2.2 依赖管理三要素

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

2.3 依赖配置

2.3.1 依赖配置-go.mod

image.png

image.png

2.3.2 依赖配置-version

image.png

2.3.3 依赖配置-indirect

image.png

image.png

2.3.4 依赖配置-incompatible

image.png

  • 主板吧2+模块会在模块路径增加/vN后缀
  • 对于没有 go.mod 文件并且主版本2+的依赖,会+incompatible

2.3.5 依赖配置-配置图

image.png

2.4 依赖分发

2.4.1 依赖分发-回源

image.png

  • 无法保证构建稳定性:增加/修改/删除软件版本
  • 无法保证依赖可用性:删除软件
  • 增加第三方压力:代码托管平台负载问题

2.4.2 依赖分发-Proxy

image.png

2.4.3 依赖分发-变量GOPROXY

image.png

2.5 工具

2.5.1 工具-go get

image.png

2.5.2 工具-go mod

image.png