入门 Go 语言-工程实战| 青训营笔记

93 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

入门 Go 语言-工程实战

协程

如何开启携程:在调用函数时在函数前添加go关键字

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

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

通道

通过make(chan 元素类型,[缓冲大小])来创建通道

  • 无缓冲通道make(chan int)
  • 有缓冲通道make(chan int,2) 子协程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 {
      fmt.Println(i)
   }
}

并发安全锁🔒

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

package main

import (
   "fmt"
   "sync"
   "time"
)

var x int64
var 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)
   fmt.Println("Withoutlock", x)
   x = 0
   for i := 0; i < 5; i++ {
      go addWithLock()
   }
   time.Sleep(time.Second)
   fmt.Println("WithLock", x)
}
func main() {
   add()
}

我们会发现有安全锁的输出了10000没有安全锁的输出了8382

等待组(WaitGroup)

WaitGroup提供的三个方法

  • Add(delta int)计数器+delta
  • Done()计数器-1
  • Wait()阻塞知道计数器为0
func hello(i int) {
   println("hello goroutine:", fmt.Sprint(i))
}
func MabyGoWait() {
   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()
}

依赖管理

GOPATH

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

弊端

  • 无法实现package的多版本控制

GO Vendor

  • 项目目录下增加vendor文件,所有依赖包副本形式放在$ProjectRoot/vendor
  • 依赖寻址方式:vendor => GOPATH
  • 通过每个项目引入一份依赖的副本,解决了多个项目需要同一个package依赖的冲突问题

弊端

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

GO Module

  • 通过go.mod文件管理依赖包版本
  • 通过go get/go mod指令工具管理依赖包
  • 终极目标:定义版本规则和管理项目依赖关系

依赖管理三要素

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

依赖配置go.mode

module github.com/wangkechun/go-by-example //依赖管理基本单元

go 1.18 //原生库
//单元依赖
require (

)

依赖标识:[Module Path][Version/Pseudo-version]

依赖配置-version

语义化版本

${MAJOR}.${MINOR]}.${PATCH}

  • V1.3.0
  • V2.3.0

基于commit伪版本

  • vX.0.0-yyyymmddhhmmss-abcdefgh1234
  • vO.0.0-20220401081311-c38fb59326b7
  • v1.0.0-20201130134442-10cb98267c6c

依赖配置-indirect

对于没有直接依赖的模块用//indirect标识出来

依赖配置-incompatible

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

依赖分发

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

依赖分发-Proxy

  • 稳定
  • 可靠

依赖分发-变量 GOPROXY

  • GOPROXY="proxy1.cn, proxy2.cn, direct”
  • 服务站点URL列表,“direct”表示源站
  • proxy1->proxy2->direct

工具- go get

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

工具- go mod

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