Go进阶与依赖管理

Go进阶与依赖管理

协程

协程的使用

内存占用

  • 协程:协程的大小通常在KB级别,因为它们的实现更轻量,运行时只需要少量的堆栈空间。协程可以通过在同一线程中切换来管理多个任务,从而降低内存消耗。
  • 线程:线程的大小一般在MB级别,线程在创建时会分配固定的堆栈空间,通常是1MB或更多。这导致在创建大量线程时,内存消耗会迅速增加。

切换开销

  • 协程:切换协程的开销相对较小,因为它们是在用户态进行切换,不需要进行上下文切换,这样可以提高性能。
  • 线程:线程切换涉及内核态的上下文切换,开销相对较大,尤其是在大量线程并发时,性能下降更明显。

使用场景

  • 协程:适合I/O密集型任务,例如网络请求、文件读写等,因为它们可以有效利用空闲时间来执行其他协程。
  • 线程:适合CPU密集型任务,需要并行执行多个计算密集型操作。
go 复制代码
 package main
 ​
 import (
     "fmt"
     "time"
 )
 ​
 func hello(i int) {
     println("hello goroutinu:" + 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()
 }

协程之间的通信

协程之间的通信通常建议采用"通过通信共享内存,而不是通过共享内存来实现通信"的方式

Channel

  1. 子协程发送0-9数字
  2. 子协程计算输入数字的平方
  3. 主协程输出最后的平方数

通道的定义

  • src 通道用于传递源数据(0到9的整数)。
  • dest 通道是一个带缓冲区的通道,缓冲区大小为3,用于存储计算后的平方值。

生产者协程

  • 第一个匿名协程负责将整数从0到9发送到 src 通道。在发送完所有数据后,使用 defer close(src) 关闭 src 通道,以通知消费者没有更多数据可供处理。

消费者协程

  • 第二个匿名协程从 src 通道中读取数据,计算每个数字的平方,并将结果发送到 dest 通道。关闭 dest 通道同样是通过 defer close(dest) 来实现,以标识所有数据已经处理完毕。
go 复制代码
 package main
 ​
 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)
     }
 }
 ​
 func main() {
     CalSquare()
 }

并发安全Lock

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

  1. 共享数据的竞态条件 : 在 addWithoutLock 函数中,多个协程并行访问和修改共享变量 x,这可能导致数据竞争。在并发环境下,多个协程同时对 x 进行加1操作,可能会产生未定义的行为,最终结果可能不等于预期。
  2. 互斥锁的使用addWithLock 函数使用了 sync.Mutex 互斥锁。在对 x 进行修改时,先调用 lock.Lock() 来获取锁,这确保在修改过程中其他协程无法同时访问 x。修改完成后,调用 lock.Unlock() 释放锁。这样可以避免数据竞态,确保 x 的值是安全的。
go 复制代码
 package main
 ​
 import (
     "sync"
     "time"
 )
 ​
 var (
     x    int64
     lock sync.Mutex
 )
 ​
 func addWithoutLock() {
     for i := 0; i < 2000; i++ {
        x += 1
     }
 }
 ​
 func addWithLock() {
     for i := 0; i < 2000; i++ {
        lock.Lock()
        x += 1
        lock.Unlock()
     }
 ​
 }
 ​
 func Add() {
     x = 0
     for i := 0; i < 5; i++ {
        go addWithoutLock()
     }
     time.Sleep(time.Second)
     println("addWithoutLock:", x)
 ​
     x = 0
     for i := 0; i < 5; i++ {
        go addWithLock()
     }
     time.Sleep(time.Second)
     println("addWithLock:", x)
 ​
 }
 ​
 func main() {
     Add()
 }

结果:

addWithoutLock: 6428 addWithLock: 10000

WaitGroup

在 Go 语言中,sync.WaitGroup 是一个用于协调多个协程(goroutines)并等待它们完成的同步原语

计数管理

  • WaitGroup 可以用来跟踪正在运行的协程的数量。通过调用 Add(n) 方法可以增加计数,表示要等待的协程数量。

协程完成的通知

  • 每个协程在完成其工作后,应该调用 Done() 方法来减少计数。通常使用 defer wg.Done() 来确保在协程结束时自动调用 Done()

等待所有协程完成

  • 在主协程或其他协程中,可以调用 Wait() 方法来阻塞当前协程,直到所有被跟踪的协程都调用了 Done()。这使得程序可以在所有并发任务完成后继续执行后续逻辑
go 复制代码
 package main
 ​
 import "sync"
 ​
 func hello(i int) {
     println(i)
 }
 ​
 func ManyGoWait() {
     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()
 }
 ​
 func main() {
     ManyGoWait()
 }

依赖管理

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

Go Module

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

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

依赖配置Version:

分为语义化版本和基于Commit伪版本

依赖分发:

回源:相当于发送到GitHub等平台仓库

Proxy:增加一个代理,这个代理可以自己去GitHub,SVN等找

变量GOPROXY:自己定义服务站点url去找依赖

工具:

go get example.org/pkg:

  • update 默认
  • none 删除
  • v1.1.2 tag版本
  • 23dx x x 特定的commit
  • master 分支最新Commit

go mod

  • init 初始化
  • download 下载模块
  • tidy 增加/删除需要的依赖

测试

  • 所有测试文件以_test.go结尾
  • func Testxxx(t *testing.T)
  • 初始化逻辑放到TestMain
go 复制代码
 func HelloTom() string {
     return "CXK"
 }
 ​
 func TestHelloTom(t *testing.T) {
     output := HelloTom()
     excepted := "CXK1"
     if output != excepted {
         t.Errorf("output: %s, excepted: %s", output, excepted)
     }
 }

单元测试assert

go 复制代码
 import (
     "github.com/stretchr/testify/assert"
     "testing"
 )
 ​
 func TestHelloTom(t *testing.T) {
     output := HelloTom()
     excepted := "CXK"
     assert.Equal(t, excepted, output)
 }
相关推荐
Find24 天前
MaxKB 集成langchain + Vue + PostgreSQL 的 本地大模型+本地知识库 构建私有大模型 | MarsCode AI刷题
青训营笔记
理tan王子24 天前
伴学笔记 AI刷题 14.数组元素之和最小化 | 豆包MarsCode AI刷题
青训营笔记
理tan王子24 天前
伴学笔记 AI刷题 25.DNA序列编辑距离 | 豆包MarsCode AI刷题
青训营笔记
理tan王子24 天前
伴学笔记 AI刷题 9.超市里的货物架调整 | 豆包MarsCode AI刷题
青训营笔记
夭要7夜宵25 天前
分而治之,主题分片Partition | 豆包MarsCode AI刷题
青训营笔记
三六1 个月前
刷题漫漫路(二)| 豆包MarsCode AI刷题
青训营笔记
tabzzz1 个月前
突破Zustand的局限性:与React ContentAPI搭配使用
前端·青训营笔记
Serendipity5651 个月前
Go 语言入门指南——单元测试 | 豆包MarsCode AI刷题;
青训营笔记
wml1 个月前
前端实践-使用React实现简单代办事项列表 | 豆包MarsCode AI刷题
青训营笔记
用户44710308932421 个月前
详解前端框架中的设计模式 | 豆包MarsCode AI刷题
青训营笔记