go语言进阶 | 青训营笔记

42 阅读2分钟

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

语言进阶

Goroutine

协程

func hello(j int){
    fmt.Println(j)
}
func HelloGoroutine(){
    for i:=0;i<5;i++{
        go func(j int){
            hello(j)
        }(j)
    }
    time.Sleep(time.Second)   //防止携程晚于主线程退出
}

Channel

  • 通过通信共享内存,而不是通过共享内存而实现通信(互斥量加锁)

即channel

  • Channel

    • make(chan 元素类型,[缓冲大小])
    • 无缓冲通道make(chan int) 同步通道,有阻塞
    • 有缓冲通道make(chan int,2) 满了阻塞

image-20230116183609872

//通信实现共享内存
/* 
 * A子携程发送0-8数字
 * B子携程计算输入数字的平方
 * 主协程输出最后的平方数字
 */
func CalSquar(){
    //不带缓冲和带缓冲
    src := make(chan int)
    dest := make(chan int,3)
    
    go func(){
        defer close(src)
        for i:=0;i<10;i++{   // A
            src <- i
        }
    }()
    
    go func(){
        defer close(dest)
        for i := range src{    //B
            dest <- i*i
        }
    }()
    
    for i := range dest{
        //
        println(i)
    }
}

并发安全Sync

//锁
var lock sync.Mutex
lock.Lock()
lock.Unlock()

WaitGroup协程同步Sync

  • WaitGroup包
Add(delta int)  //计数器+delta
Done()          //计数器-1
Wait()          //阻塞直到计数器为0
func hello(j int){
    fmt.Println(j)
}
func HelloGoroutine(){
    var wg sync.WaitGroup
    wg.Add(5)                     //设置协程个数
    for i:=0;i<5;i++{
        go func(j int){
            wg.Done()     //-1
            hello(j)
        }(j)
    }
    wg.Wait()        //防止携程晚于主线程退出
}

TODO :测试如果线程早于协程(sleep)退出会发生什么

依赖管理

GOPATH

  1. go的工作区

    目录结构(go的库位置)/home/ubuntu/go

    bin
    pkg
    src   //源码存在的位置
    
  2. 缺点:因为是GO的环境变量,所以所有的项目都使用同一个PATH,依赖同一个版本的Go包,无法实现不同项目的版本管理

Go Vendor

  1. 项目目录下增加vendor文件,所有依赖包副本形式放在$ProjectRoot/vendor

依赖寻址方式:vendor=>GOPATH

  1. 无法实现一个项目不同的模块依赖不同的版本

Go Module

  • 通过go.mod文件管理
  • 通过go get/go mod指令工具管理依赖
  1. 依赖管理三要素
  1. 配置文件,描述依赖 go.mod
  2. 中心仓库管理依赖库 Proxy
  3. 本地工具 go get/go mod
  1. go.mod

    • 对于go代码中的import,实际上需要的是module模块,即package的路径,因此尽量让文件夹名字与package name一致 ,如果不一致,go.mod中的module ....goimport ...中对应都填路径(路径要求以域名的形式给出),.go中调用时用package name

image-20230116223410893.png

//依赖管理基本单元
module github.com/Moonlight-Zhao/go-project-example
​
go 1.16require (//依赖
    github.com/gin-contrib/sse v0.1.0 // indirect 间接依赖
    github.com/gin-gonic/gin v1.3.0 // indirect
    github.com/go-playground/validator/v10 v10.10.0 // indirect
    github.com/goccy/go-json v0.9.6 // indirect
    github.com/golang/protobuf v1.5.2 // indirect
    github.com/jinzhu/now v1.1.5 // indirect
    github.com/json-iterator/go v1.1.12 // indirect
    github.com/mattn/go-isatty v0.0.14 // indirect
    github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
    github.com/stretchr/testify v1.7.1 // indirect
    github.com/ugorji/go v1.2.7 // indirect
    go.uber.org/atomic v1.9.0 // indirect
    go.uber.org/multierr v1.8.0 // indirect
    go.uber.org/zap v1.21.0
    golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
    golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f // indirect
    google.golang.org/protobuf v1.28.0 // indirect
    gopkg.in/gin-gonic/gin.v1 v1.3.0
    gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
    gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
    gopkg.in/yaml.v2 v2.4.0 // indirect
    gorm.io/driver/mysql v1.3.3
    gorm.io/gorm v1.23.4
)
  1. 依赖版本

语义化版本

V1.3.0 git tag

基于commit的伪版本

vx.0.0-yyyymmddhhmmss-abcdadfa1234

依赖分发

  • 直接依赖第三方线上库,会因为网络和线上资源问题出现各种问题

使用Proxy

GOPROXY="https://.....",从前往后代理

go get

go get example.org/pkg ...
+
@update   默认
@none     删除依赖
@v1.1.2   tag版本,语义版本
@23434f   特定提交
@master   分支最新commit

go mod

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

测试

单元测试

  • 所有的测试文件以_test.go结尾

  • 测试函数要求

    func TestXxx(t* testing.T)
    
  • 初始化逻辑放到TestMain

    func TestMain(m *testing.M){
        //测试前:数据装载、配置初始化等
        
        //跑当前package下的所有单元测试(所有的_test.go文件)
        code := m.Run() 
        
        //测试后:资源释放//退出
        os.Exit(code)
    }
    
  • 例子

func HelloTom() string{
    return "Jerry"
}
​
---------
​
func TestHelloTom(t *testing.T){
    output := HelloTom()
    expectOutput := "Tom"
    if output != expectOutput{
        t.Errorf("Excepted %s, but %s\n",expectOutput,output)
    }
}
​
------测试运行
go test [flags] [packages]
go test judgment_test.go judgement.go [--cover]
  • assert 包
import "github.com/stretchr/testify/assert"

mock测试

monkey : https://github.com/bouk/monkey

快速Mock函数

  • 为一个函数打桩(将一个函数替换为另外一个函数,用于测试)

    func TestProcessFirstLineWithMock(t *testing.T){
        monkey.Patch(ReadFirstLine,func()string{
            return "line110"
        })//将第一个函数进行替换为第二个参数
        defer monkey.Unpatch(ReadFirstLine)  //取消打桩
        line := ProcessFirstLine()
        assert.Equal(t,"line000",line)
    }
    

基准测试

  • 用于性能分析
func BenchmarkSelect(b *testing.B){
    b.ResetTimer()
    for i:=0;i<b.N;i++{
        Select()  //被测试的函数
    }
}
​
func BenchmarkParallel(b *testing.B){
    b.ResetTimer()
    b.RunParallel(func(pb *testing.PB){
        for pb.Next(){
            Select()
        }
    })
}
-------
go test -bench=.