【Go语言进阶 | 青训营笔记】

90 阅读1分钟

Go语言进阶

这是我参与【第五届青训营】伴学笔记创作活动的第二天,此篇概述一下在青训营学习的go语言的部分进阶知识点。

  1. 并发 vs 并行

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

    • go 语言采用协程(goroutine), 一个线程跑多个协程,相比于线程占用更少的运行空间 ,提高了并发性

    • // 简单的开启协程的方法 (直接用go)func hello(j int) {
          fmt.Println("hello goroutine ", j)
      }
      func HelloGoRoutine() {
          for i := 0; i < 5; i++ {
              go func(j int){
                  hello(j) 
              }(i)
          }
          time.Sleep(time.Second)
      }
      

      运行结果:

      4.png

  1. 通道的使用

    • 提倡通信共享内存而不是通过共享内存而实现通信,采用了 channel

    • Channel 的使用方法

      make(chan type, [缓冲大小])

      • 无缓冲通道:make(chan int) (同步通道)
      • 有缓冲通道:make(chan int, 2) (阻塞+运行)
    • 简单应用

      func CalSquare() {
          src := make(chan int)
          dest := make(chan int3)
          
          // 生产数字
          go func() {
              defer close(src)
              for i := 0; i < 10; i++ {
                  src <- i  // 生产i
              }
          }
          // 消费数字
          go func() {
              defer close(dest)
              for i :=range src {
                  dest <- i * i  // 消费i,记录i*i
              }
          }()
          // 输出结果
          for i := range dest {
              println(i)
          }
      }
      

  1. 并发安全 Lock

    • 简单加锁的方法:

      var (
          x    int64
          lock sync.Mutex
      )
      ​
      func addWithOutLock() {
          for i := 0; i < 2000; i++ {
              x += 1
          }
      }
      ​
      func main() {
          x = 0
          for i := 0; i < 5; i++ {
              go addWithOutLock()
          }
          time.Sleep(time.Second)
          fmt.Println(x)
      }
      

      3.png

      结果分析:未加锁时,由于五个协程并行的原因,可能当一个协程读取原来的x, 而另一个协程已经将原来的 x 加 1,即原来的 x 发生改变,此时第一个进程加1后写入,覆盖掉了第二个进程的加1操作,即出现 x 的大小小于 10000

    • WaitGroup 优化

      func MangGowait() {
          var wg sync.WaitGroup
          wg.Add(5)                   // 计数器增加 5 
          for i := 0; i < 5; i++ {
              go func(j int) {
                  defer wg.Done()     // 计数器减 1
                  hello(j)
              }(i)
          }
          wg.Wait()  // 等待结束
      }
      

  1. 依赖管理

    • 作用:定义版本规则和管理项目依赖关系

    • go 依赖管理推进:gopath -> go vendor -> go module

    • 依赖管理三要素:

      • 配置文件,描述依赖: go.mod
      • 中心仓库管理依赖: Proxy
      • 本地工具 : go get / mod
    • version:语义化版本/ 基于commit 伪版本

    • indirect 非直接依赖

    • 选择兼容低版本

  1. 依赖分发

    1.png 2.png

    图一为回源,图二为proxy

    采用 Proxy 的优点:稳定,可靠,缓解压力

  1. go getgo mod

    go get example.org/pkg
    @update  // 默认
    @none    // 去除依赖
    @v1.1.2  // 语义版本
    @23dfdd5 // 特定的commit
    @master  // 分支的最新的commit
    ​
    go mod
    init     // 初始化,创建 go.mod文件
    download // 下载模块到本地缓存
    tidy     // 增加需要的依赖,去除不需要的依赖