青训营X豆包MarsCode 技术训练Day2——Go语言入门-工程实践

30 阅读4分钟

Go语言入门-工程实践

语言进阶

以并发编程的视角了解Go语言高性能的本质

  • 并发VS并行

线程和协程

  • 协程:用户态的,轻量级线程,是KB级别的,所以适合高并发开发

eg:

快速打印hello十次——开启协程打印

package main
​
import (
  "fmt"
  "time"
)
​
func main() {
  HelloGoRoutine()
}
​
func hello(i int) {
  println("hello", fmt.Sprint(i))
}
​
func HelloGoRoutine() {
  for i := 0; i < 10; i++ {
    //  使用go开启协程
    go func(j int) {
      hello(j)
    }(i)
  }
  time.Sleep(time.Second)
}
​

加上时间暂停的目的是为了防止子协程还没有结束了的情况下主协程就已经退出,也就是java中的main线程自动退出的类似情况

CSP——协程中的通信

image.png

  • go语言通过通信来共享内存,也就是协程之间互相单方面传递信息

数据与数据之间通过channel来传递

channel

实现语法:

make(chan 元素类型,[缓冲大小])

  • 有缓冲通道的channel
  • 无缓冲通道的channel

image.png 实现channel的例子:

func CalSquare() {
  src := make(chan int)
  //制造一个有缓冲的channel传回去给main协程
  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)
  }
}
​

解释几个奇怪的地方:

  1. <-操作
在 Go 语言中,src <- I 不是赋值语句,而是一个通道(channel)操作,用于向通道 src 发送值 I。
​
具体说明
​
通道发送操作:src <- I 表示将值 I 发送到通道 src 中。这是通道的一个重要特性,允许 goroutines 之间进行通信。
​
发送值:当这条语句执行时,发送的值会被放入 src 通道,其他在接收这个通道的 goroutine 可以通过 value := <-src 的方式接收到这个值。
​
阻塞行为:在发送值时,如果通道是无缓冲的,则发送操作会阻塞,直到有其他 goroutine 从该通道接收值。如果通道是有缓冲的,发送操作会在缓冲区未满的情况下执行。

2. 为什么第二个要使用有缓冲的队列

因为生产的逻辑易于消费的逻辑,所以带缓冲的就不会因为几个数据同时进入而造成堵塞或者其他的问题从而影响生产效率

3. 这样的channel保证了数据传输的过程中的并发安全,通过通讯来实现了共享内存的机制

  1. defer close(src)是上节课说过的执行结束以后关闭channel的写法,有点类似于java中输出输入流的close

  2. defer关键字是在函数获得返回值以后才会执行的语句

并发安全Lock

image.png

  • 因为之前也说了,go语言中还保留了通过共享来实现通讯的机制,所以会出现java多线程中经典的超卖漏买问题

WaitGroup

  • 使用WaitGroup来管理我们的子线程

image.png

前边我们使用子线程强制暂停的方法来等待主线程结束,这样的方式显然不太合理也不太优雅

WaitGroup就可以管理我们的子线程,waitgroup底层维护的是一个计数器

当所有的子线程都为0以后,主线程才会跳出

// ManyGoWait 使用WaitGroup进行优化
func ManyGoWait() {
  var wg sync.WaitGroup
  wg.Add(5)
  for i := 0; i < 5; i++ {
    //  使用go开启协程
    go func(j int) {
      //子线程-1
      defer wg.Done()
      hello(j)
    }(i)
  }
  //  等所有线程结束以后fun才推出
  wg.Wait()
}

go语言的依赖管理

GOPATH

  • 项目代码都在src下
  • go get下载新版本的包都在src目录下

Go Vendor

  • 在项目目录下增加vendor文件,所有依赖包副本形式放在vendor文件夹下

Go Module

  • 默认开启

依赖管理三要素

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

image.png

依赖配置-version
  • 语义化版本

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

    major是大版本,不同的major版本是代码隔离的

    minor是前后兼容,其实是加了一些函数

    patch一般是代码版本的修复

  • 基于commit的伪版本

    版本号-时间戳-哈希码

  • 选择最低的兼容版本

image.png